mirror of
https://github.com/IT4Change/gradido.git
synced 2026-03-01 12:44:43 +00:00
Merge branch 'master' into fix_seeds
This commit is contained in:
commit
ecfeeadcb1
15
.github/workflows/lint.yml
vendored
15
.github/workflows/lint.yml
vendored
@ -12,6 +12,7 @@ jobs:
|
||||
backend: ${{ steps.backend.outputs.success }}
|
||||
database: ${{ steps.database.outputs.success }}
|
||||
dht-node: ${{ steps.dht-node.outputs.success }}
|
||||
dlt-connector: ${{ steps.dlt-connector.outputs.success }}
|
||||
federation: ${{ steps.federation.outputs.success }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -57,6 +58,12 @@ jobs:
|
||||
cd ./dht-node
|
||||
biome ci .
|
||||
echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||
- name: Lint - DLT Connector
|
||||
id: dlt-connector
|
||||
run: |
|
||||
cd ./dlt-connector
|
||||
biome ci .
|
||||
echo "success=$([ $? -eq 0 ] && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||
- name: Lint - Federation
|
||||
id: federation
|
||||
run: |
|
||||
@ -112,6 +119,14 @@ jobs:
|
||||
- name: Check result from previous step
|
||||
run: if [ "${{ needs.lint.outputs.dht-node }}" != "true" ]; then exit 1; fi
|
||||
|
||||
lint_dlt_connector:
|
||||
name: Lint - DLT Connector
|
||||
needs: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check result from previous step
|
||||
run: if [ "${{ needs.lint.outputs.dlt-connector }}" != "true" ]; then exit 1; fi
|
||||
|
||||
lint_federation:
|
||||
name: Lint - Federation
|
||||
needs: lint
|
||||
|
||||
57
.github/workflows/test_dlt_connector.yml
vendored
57
.github/workflows/test_dlt_connector.yml
vendored
@ -28,29 +28,20 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: create .env
|
||||
run: |
|
||||
cd dlt-connector
|
||||
cat <<EOF > .env
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=$(openssl rand -hex 16)
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=$(openssl rand -hex 16)
|
||||
HOME_COMMUNITY_SEED=$(openssl rand -hex 32)
|
||||
HIERO_OPERATOR_KEY=$(openssl rand -hex 32)
|
||||
HIERO_OPERATOR_ID="0.0.2"
|
||||
EOF
|
||||
|
||||
- name: Build 'test' image
|
||||
run: |
|
||||
docker build --target test -t "gradido/dlt-connector:test" -f dlt-connector/Dockerfile .
|
||||
docker save "gradido/dlt-connector:test" > /tmp/dlt-connector.tar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docker-dlt-connector-test
|
||||
path: /tmp/dlt-connector.tar
|
||||
|
||||
lint:
|
||||
name: Lint - DLT Connector
|
||||
if: needs.files-changed.outputs.dlt_connector == 'true'
|
||||
needs: files-changed
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Lint
|
||||
run: cd dlt-connector && yarn && yarn run lint
|
||||
run: docker build --target production -t "gradido/dlt-connector:productionTest" -f dlt-connector/Dockerfile .
|
||||
|
||||
unit_test:
|
||||
name: Unit Tests - DLT Connector
|
||||
@ -60,13 +51,21 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: DLT-Connector | docker-compose mariadb
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb
|
||||
|
||||
- name: install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version-file: '.bun-version'
|
||||
|
||||
- name: Sleep for 30 seconds
|
||||
run: sleep 30s
|
||||
shell: bash
|
||||
- name: install dependencies
|
||||
run: cd dlt-connector && bun install --frozen-lockfile
|
||||
|
||||
- name: typecheck && unit test
|
||||
run: |
|
||||
export GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=$(openssl rand -hex 16)
|
||||
export GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=$(openssl rand -hex 16)
|
||||
export HOME_COMMUNITY_SEED=$(openssl rand -hex 32)
|
||||
export HIERO_OPERATOR_KEY=$(openssl rand -hex 32)
|
||||
export HIERO_OPERATOR_ID="0.0.2"
|
||||
cd dlt-connector && bun typecheck && bun test
|
||||
|
||||
- name: DLT-Connector | Unit tests
|
||||
run: cd dlt-database && yarn && yarn build && cd ../dlt-connector && yarn && yarn test
|
||||
|
||||
@ -1,49 +1,10 @@
|
||||
import { Transaction as DbTransaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { DltConnectorClient } from './DltConnectorClient'
|
||||
|
||||
let con: DataSource
|
||||
|
||||
let testEnv: {
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
// Mock the GraphQLClient
|
||||
jest.mock('graphql-request', () => {
|
||||
const originalModule = jest.requireActual('graphql-request')
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
GraphQLClient: jest.fn().mockImplementation((url: string) => {
|
||||
if (url === 'invalid') {
|
||||
throw new Error('invalid url')
|
||||
}
|
||||
return {
|
||||
// why not using mockResolvedValueOnce or mockReturnValueOnce?
|
||||
// I have tried, but it didn't work and return every time the first value
|
||||
request: jest.fn().mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
transmitTransaction: {
|
||||
succeed: true,
|
||||
},
|
||||
})
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
describe('undefined DltConnectorClient', () => {
|
||||
it('invalid url', () => {
|
||||
CONFIG.DLT_CONNECTOR_URL = 'invalid'
|
||||
CONFIG.DLT_CONNECTOR_URL = ''
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
const result = DltConnectorClient.getInstance()
|
||||
expect(result).toBeUndefined()
|
||||
@ -58,90 +19,4 @@ describe('undefined DltConnectorClient', () => {
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
describe.skip('transmitTransaction, without db connection', () => {
|
||||
const transaction = new DbTransaction()
|
||||
transaction.typeId = 2 // Example transaction type ID
|
||||
transaction.amount = new Decimal('10.00') // Example amount
|
||||
transaction.balanceDate = new Date() // Example creation date
|
||||
transaction.id = 1 // Example transaction ID
|
||||
|
||||
it('cannot query for transaction id', async () => {
|
||||
const result = await DltConnectorClient.getInstance()?.transmitTransaction(transaction)
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
describe('transmitTransaction', () => {
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
const transaction = new DbTransaction()
|
||||
transaction.typeId = 2 // Example transaction type ID
|
||||
transaction.amount = new Decimal('10.00') // Example amount
|
||||
transaction.balanceDate = new Date() // Example creation date
|
||||
transaction.id = 1 // Example transaction ID
|
||||
|
||||
// data needed to let save succeed
|
||||
transaction.memo = "I'm a dummy memo"
|
||||
transaction.userId = 1
|
||||
transaction.userGradidoID = 'dummy gradido id'
|
||||
|
||||
/*
|
||||
it.skip('cannot find transaction in db', async () => {
|
||||
const result = await DltConnectorClient.getInstance()?.transmitTransaction(transaction)
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
*/
|
||||
|
||||
it('invalid transaction type', async () => {
|
||||
const localTransaction = new DbTransaction()
|
||||
localTransaction.typeId = 12
|
||||
try {
|
||||
await DltConnectorClient.getInstance()?.transmitTransaction(localTransaction)
|
||||
} catch (e) {
|
||||
expect(e).toMatchObject(
|
||||
new LogError(`invalid transaction type id: ${localTransaction.typeId.toString()}`),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
it.skip('should transmit the transaction and update the dltTransactionId in the database', async () => {
|
||||
await transaction.save()
|
||||
|
||||
const result = await DltConnectorClient.getInstance()?.transmitTransaction(transaction)
|
||||
expect(result).toBe(true)
|
||||
})
|
||||
|
||||
it.skip('invalid dltTransactionId (maximal 32 Bytes in Binary)', async () => {
|
||||
await transaction.save()
|
||||
|
||||
const result = await DltConnectorClient.getInstance()?.transmitTransaction(transaction)
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
/*
|
||||
describe.skip('try transmitTransaction but graphql request failed', () => {
|
||||
it('graphql request should throw', async () => {
|
||||
const transaction = new DbTransaction()
|
||||
transaction.typeId = 2 // Example transaction type ID
|
||||
transaction.amount = new Decimal('10.00') // Example amount
|
||||
transaction.balanceDate = new Date() // Example creation date
|
||||
transaction.id = 1 // Example transaction ID
|
||||
const result = await DltConnectorClient.getInstance()?.transmitTransaction(transaction)
|
||||
expect(result).toBe(false)
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
@ -1,36 +1,12 @@
|
||||
import { Transaction as DbTransaction } from 'database'
|
||||
import { GraphQLClient, gql } from 'graphql-request'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { TransactionTypeId } from 'core'
|
||||
import { LogError } from '@/server/LogError'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { TransactionResult } from './model/TransactionResult'
|
||||
import { UserIdentifier } from './model/UserIdentifier'
|
||||
import { TransactionDraft } from './model/TransactionDraft'
|
||||
import { IRestResponse, RestClient } from 'typed-rest-client'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.dltConnector`)
|
||||
|
||||
const sendTransaction = gql`
|
||||
mutation ($input: TransactionInput!) {
|
||||
sendTransaction(data: $input) {
|
||||
dltTransactionIdHex
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// from ChatGPT
|
||||
function getTransactionTypeString(id: TransactionTypeId): string {
|
||||
const key = Object.keys(TransactionTypeId).find(
|
||||
(key) => TransactionTypeId[key as keyof typeof TransactionTypeId] === id,
|
||||
)
|
||||
if (key === undefined) {
|
||||
throw new LogError('invalid transaction type id: ' + id.toString())
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
||||
// and ../federation/client/FederationClientFactory.ts
|
||||
/**
|
||||
@ -40,7 +16,7 @@ function getTransactionTypeString(id: TransactionTypeId): string {
|
||||
|
||||
export class DltConnectorClient {
|
||||
private static instance: DltConnectorClient
|
||||
client: GraphQLClient
|
||||
client: RestClient
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
@ -64,13 +40,12 @@ export class DltConnectorClient {
|
||||
}
|
||||
if (!DltConnectorClient.instance.client) {
|
||||
try {
|
||||
DltConnectorClient.instance.client = new GraphQLClient(CONFIG.DLT_CONNECTOR_URL, {
|
||||
method: 'GET',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
DltConnectorClient.instance.client = new RestClient(
|
||||
'gradido-backend',
|
||||
CONFIG.DLT_CONNECTOR_URL,
|
||||
undefined,
|
||||
{ keepAlive: true }
|
||||
)
|
||||
} catch (e) {
|
||||
logger.error("couldn't connect to dlt-connector: ", e)
|
||||
return
|
||||
@ -80,47 +55,14 @@ export class DltConnectorClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* transmit transaction via dlt-connector to iota
|
||||
* and update dltTransactionId of transaction in db with iota message id
|
||||
* transmit transaction via dlt-connector to hiero
|
||||
* and update dltTransactionId of transaction in db with hiero transaction id
|
||||
*/
|
||||
public async transmitTransaction(transaction: DbTransaction): Promise<boolean> {
|
||||
const typeString = getTransactionTypeString(transaction.typeId)
|
||||
// no negative values in dlt connector, gradido concept don't use negative values so the code don't use it too
|
||||
const amountString = transaction.amount.abs().toString()
|
||||
const params = {
|
||||
input: {
|
||||
user: {
|
||||
uuid: transaction.userGradidoID,
|
||||
communityUuid: transaction.userCommunityUuid,
|
||||
} as UserIdentifier,
|
||||
linkedUser: {
|
||||
uuid: transaction.linkedUserGradidoID,
|
||||
communityUuid: transaction.linkedUserCommunityUuid,
|
||||
} as UserIdentifier,
|
||||
amount: amountString,
|
||||
type: typeString,
|
||||
createdAt: transaction.balanceDate.toISOString(),
|
||||
backendTransactionId: transaction.id,
|
||||
targetDate: transaction.creationDate?.toISOString(),
|
||||
},
|
||||
}
|
||||
try {
|
||||
// TODO: add account nr for user after they have also more than one account in backend
|
||||
logger.debug('transmit transaction to dlt connector', params)
|
||||
const {
|
||||
data: {
|
||||
sendTransaction: { error, succeed },
|
||||
},
|
||||
} = await this.client.rawRequest<{ sendTransaction: TransactionResult }>(
|
||||
sendTransaction,
|
||||
params,
|
||||
)
|
||||
if (error) {
|
||||
throw new Error(error.message)
|
||||
}
|
||||
return succeed
|
||||
} catch (e) {
|
||||
throw new LogError('Error send sending transaction to dlt-connector: ', e)
|
||||
}
|
||||
public async sendTransaction(input: TransactionDraft): Promise<IRestResponse<{ transactionId: string }>> {
|
||||
logger.debug('transmit transaction or user to dlt connector', input)
|
||||
return await this.client.create<{ transactionId: string }>(
|
||||
'/sendTransaction',
|
||||
input
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
import { CONFIG } from '@/config'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
import { TransactionDraft } from '@/apis/dltConnector/model/TransactionDraft'
|
||||
import { IRestResponse } from 'typed-rest-client'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.apis.dltConnector`)
|
||||
|
||||
// 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.
|
||||
*/
|
||||
|
||||
export class DltConnectorClient {
|
||||
private static instance: DltConnectorClient
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(): DltConnectorClient | undefined {
|
||||
if (!CONFIG.DLT_CONNECTOR || !CONFIG.DLT_CONNECTOR_URL) {
|
||||
logger.info(`dlt-connector are disabled via config...`)
|
||||
return
|
||||
}
|
||||
if (!DltConnectorClient.instance) {
|
||||
DltConnectorClient.instance = new DltConnectorClient()
|
||||
}
|
||||
return DltConnectorClient.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* transmit transaction via dlt-connector to hiero
|
||||
* and update dltTransactionId of transaction in db with hiero transaction id
|
||||
*/
|
||||
public async sendTransaction(input: TransactionDraft): Promise<IRestResponse<string>> {
|
||||
logger.debug('transmit transaction or user to dlt connector', input)
|
||||
return Promise.resolve({
|
||||
statusCode: 200,
|
||||
result: 'test',
|
||||
headers: {},
|
||||
})
|
||||
}
|
||||
}
|
||||
9
backend/src/apis/dltConnector/enum/AccountType.ts
Normal file
9
backend/src/apis/dltConnector/enum/AccountType.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export enum AccountType {
|
||||
NONE = 'NONE', // if no address was found
|
||||
COMMUNITY_HUMAN = 'COMMUNITY_HUMAN', // creation account for human
|
||||
COMMUNITY_GMW = 'COMMUNITY_GMW', // community public budget account
|
||||
COMMUNITY_AUF = 'COMMUNITY_AUF', // community compensation and environment founds account
|
||||
COMMUNITY_PROJECT = 'COMMUNITY_PROJECT', // no creations allowed
|
||||
SUBACCOUNT = 'SUBACCOUNT', // no creations allowed
|
||||
CRYPTO_ACCOUNT = 'CRYPTO_ACCOUNT', // user control his keys, no creations
|
||||
}
|
||||
9
backend/src/apis/dltConnector/enum/DltTransactionType.ts
Normal file
9
backend/src/apis/dltConnector/enum/DltTransactionType.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export enum DltTransactionType {
|
||||
UNKNOWN = 0,
|
||||
REGISTER_ADDRESS = 1,
|
||||
CREATION = 2,
|
||||
TRANSFER = 3,
|
||||
DEFERRED_TRANSFER = 4,
|
||||
REDEEM_DEFERRED_TRANSFER = 5,
|
||||
DELETE_DEFERRED_TRANSFER = 6,
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Error Types for dlt-connector graphql responses
|
||||
*/
|
||||
export enum TransactionErrorType {
|
||||
NOT_IMPLEMENTED_YET = 'Not Implemented yet',
|
||||
MISSING_PARAMETER = 'Missing parameter',
|
||||
ALREADY_EXIST = 'Already exist',
|
||||
DB_ERROR = 'DB Error',
|
||||
PROTO_DECODE_ERROR = 'Proto Decode Error',
|
||||
PROTO_ENCODE_ERROR = 'Proto Encode Error',
|
||||
INVALID_SIGNATURE = 'Invalid Signature',
|
||||
LOGIC_ERROR = 'Logic Error',
|
||||
NOT_FOUND = 'Not found',
|
||||
}
|
||||
@ -1,11 +1,19 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
/**
|
||||
* Transaction Types on Blockchain
|
||||
*/
|
||||
export enum TransactionType {
|
||||
GRADIDO_TRANSFER = 1,
|
||||
GRADIDO_CREATION = 2,
|
||||
GROUP_FRIENDS_UPDATE = 3,
|
||||
REGISTER_ADDRESS = 4,
|
||||
GRADIDO_DEFERRED_TRANSFER = 5,
|
||||
COMMUNITY_ROOT = 6,
|
||||
GRADIDO_TRANSFER = 'GRADIDO_TRANSFER',
|
||||
GRADIDO_CREATION = 'GRADIDO_CREATION',
|
||||
GROUP_FRIENDS_UPDATE = 'GROUP_FRIENDS_UPDATE',
|
||||
REGISTER_ADDRESS = 'REGISTER_ADDRESS',
|
||||
GRADIDO_DEFERRED_TRANSFER = 'GRADIDO_DEFERRED_TRANSFER',
|
||||
GRADIDO_REDEEM_DEFERRED_TRANSFER = 'GRADIDO_REDEEM_DEFERRED_TRANSFER',
|
||||
COMMUNITY_ROOT = 'COMMUNITY_ROOT',
|
||||
}
|
||||
|
||||
registerEnumType(TransactionType, {
|
||||
name: 'TransactionType', // this one is mandatory
|
||||
description: 'Type of the transaction', // this one is optional
|
||||
})
|
||||
|
||||
164
backend/src/apis/dltConnector/index.ts
Normal file
164
backend/src/apis/dltConnector/index.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import { IRestResponse } from 'typed-rest-client'
|
||||
import { DltTransactionType } from './enum/DltTransactionType'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { DltConnectorClient } from './DltConnectorClient'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
Contribution as DbContribution,
|
||||
DltTransaction as DbDltTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
getCommunityByUuid,
|
||||
getHomeCommunity,
|
||||
getUserById,
|
||||
UserLoggingView,
|
||||
} from 'database'
|
||||
import { TransactionDraft } from './model/TransactionDraft'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.dltConnector`)
|
||||
// will be undefined if dlt connect is disabled
|
||||
const dltConnectorClient = DltConnectorClient.getInstance()
|
||||
|
||||
async function checkDltConnectorResult(dltTransaction: DbDltTransaction, clientResponse: Promise<IRestResponse<{ transactionId: string }>>)
|
||||
: Promise<DbDltTransaction> {
|
||||
// check result from dlt connector
|
||||
try {
|
||||
const response = await clientResponse
|
||||
if (response.statusCode === 200 && response.result) {
|
||||
dltTransaction.messageId = response.result.transactionId
|
||||
} else {
|
||||
dltTransaction.error = `empty result with status code ${response.statusCode}`
|
||||
logger.error('error from dlt-connector', response)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.debug(e)
|
||||
if (e instanceof Error) {
|
||||
dltTransaction.error = e.message
|
||||
} else if (typeof e === 'string') {
|
||||
dltTransaction.error = e
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return dltTransaction
|
||||
}
|
||||
|
||||
async function executeDltTransaction(draft: TransactionDraft | null, typeId: DltTransactionType, persist = true): Promise<DbDltTransaction | null> {
|
||||
if (draft && dltConnectorClient) {
|
||||
const clientResponse = dltConnectorClient.sendTransaction(draft)
|
||||
let dltTransaction = new DbDltTransaction()
|
||||
dltTransaction.typeId = typeId
|
||||
dltTransaction = await checkDltConnectorResult(dltTransaction, clientResponse)
|
||||
if (persist) {
|
||||
return await dltTransaction.save()
|
||||
}
|
||||
return dltTransaction
|
||||
}
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* send register address transaction via dlt-connector to hiero
|
||||
* and update dltTransactionId of transaction in db with hiero transaction id
|
||||
*/
|
||||
export async function registerAddressTransaction(user: DbUser, community: DbCommunity): Promise<DbDltTransaction | null> {
|
||||
if (!CONFIG.DLT_CONNECTOR) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
if (!user.id) {
|
||||
logger.error(`missing id for user: ${user.gradidoID}, please call registerAddressTransaction after user.save()`)
|
||||
return null
|
||||
}
|
||||
// return null if some data where missing and log error
|
||||
const draft = TransactionDraft.createRegisterAddress(user, community)
|
||||
const dltTransaction = await executeDltTransaction(draft, DltTransactionType.REGISTER_ADDRESS, false)
|
||||
if (dltTransaction) {
|
||||
if (user.id) {
|
||||
dltTransaction.userId = user.id
|
||||
}
|
||||
return await dltTransaction.save()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export async function contributionTransaction(
|
||||
contribution: DbContribution,
|
||||
signingUser: DbUser,
|
||||
createdAt: Date,
|
||||
): Promise<DbDltTransaction | null> {
|
||||
if (!CONFIG.DLT_CONNECTOR) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
const homeCommunity = await getHomeCommunity()
|
||||
if (!homeCommunity) {
|
||||
logger.error('home community not found')
|
||||
return null
|
||||
}
|
||||
const draft = TransactionDraft.createContribution(contribution, createdAt, signingUser, homeCommunity)
|
||||
return await executeDltTransaction(draft, DltTransactionType.CREATION)
|
||||
}
|
||||
|
||||
export async function transferTransaction(
|
||||
senderUser: DbUser,
|
||||
recipientUser: DbUser,
|
||||
amount: string,
|
||||
memo: string,
|
||||
createdAt: Date
|
||||
): Promise<DbDltTransaction | null> {
|
||||
if (!CONFIG.DLT_CONNECTOR) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
// load community if not already loaded, maybe they are remote communities
|
||||
if (!senderUser.community) {
|
||||
senderUser.community = await getCommunityByUuid(senderUser.communityUuid)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
recipientUser.community = await getCommunityByUuid(recipientUser.communityUuid)
|
||||
}
|
||||
logger.info(`sender user: ${new UserLoggingView(senderUser)}`)
|
||||
logger.info(`recipient user: ${new UserLoggingView(recipientUser)}`)
|
||||
const draft = TransactionDraft.createTransfer(senderUser, recipientUser, amount, memo, createdAt)
|
||||
return await executeDltTransaction(draft, DltTransactionType.TRANSFER)
|
||||
}
|
||||
|
||||
export async function deferredTransferTransaction(senderUser: DbUser, transactionLink: DbTransactionLink)
|
||||
: Promise<DbDltTransaction | null> {
|
||||
if (!CONFIG.DLT_CONNECTOR) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
// load community if not already loaded
|
||||
if (!senderUser.community) {
|
||||
senderUser.community = await getCommunityByUuid(senderUser.communityUuid)
|
||||
}
|
||||
const draft = TransactionDraft.createDeferredTransfer(senderUser, transactionLink)
|
||||
return await executeDltTransaction(draft, DltTransactionType.DEFERRED_TRANSFER)
|
||||
}
|
||||
|
||||
export async function redeemDeferredTransferTransaction(transactionLink: DbTransactionLink, amount: string, createdAt: Date, recipientUser: DbUser)
|
||||
: Promise<DbDltTransaction | null> {
|
||||
if (!CONFIG.DLT_CONNECTOR) {
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
// load user and communities if not already loaded
|
||||
if (!transactionLink.user) {
|
||||
logger.debug('load sender user')
|
||||
transactionLink.user = await getUserById(transactionLink.userId, true, false)
|
||||
}
|
||||
if (!transactionLink.user.community) {
|
||||
logger.debug('load sender community')
|
||||
transactionLink.user.community = await getCommunityByUuid(transactionLink.user.communityUuid)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
logger.debug('load recipient community')
|
||||
recipientUser.community = await getCommunityByUuid(recipientUser.communityUuid)
|
||||
}
|
||||
logger.debug(`sender: ${new UserLoggingView(transactionLink.user)}`)
|
||||
logger.debug(`recipient: ${new UserLoggingView(recipientUser)}`)
|
||||
const draft = TransactionDraft.redeemDeferredTransfer(transactionLink, amount, createdAt, recipientUser)
|
||||
return await executeDltTransaction(draft, DltTransactionType.REDEEM_DEFERRED_TRANSFER)
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
backend/src/apis/dltConnector/model/AccountIdentifier.ts
Normal file
17
backend/src/apis/dltConnector/model/AccountIdentifier.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { CommunityAccountIdentifier } from './CommunityAccountIdentifier'
|
||||
import { IdentifierSeed } from './IdentifierSeed'
|
||||
|
||||
export class AccountIdentifier {
|
||||
communityTopicId: string
|
||||
account?: CommunityAccountIdentifier
|
||||
seed?: IdentifierSeed // used for deferred transfers
|
||||
|
||||
constructor(communityTopicId: string, input: CommunityAccountIdentifier | IdentifierSeed) {
|
||||
if (input instanceof CommunityAccountIdentifier) {
|
||||
this.account = input
|
||||
} else if (input instanceof IdentifierSeed) {
|
||||
this.seed = input
|
||||
}
|
||||
this.communityTopicId = communityTopicId
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
export class CommunityAccountIdentifier {
|
||||
// for community user, uuid and communityUuid used
|
||||
userUuid: string
|
||||
accountNr?: number
|
||||
|
||||
constructor(userUuid: string, accountNr?: number) {
|
||||
this.userUuid = userUuid
|
||||
this.accountNr = accountNr
|
||||
}
|
||||
}
|
||||
9
backend/src/apis/dltConnector/model/IdentifierSeed.ts
Normal file
9
backend/src/apis/dltConnector/model/IdentifierSeed.ts
Normal file
@ -0,0 +1,9 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
|
||||
export class IdentifierSeed {
|
||||
seed: string
|
||||
|
||||
constructor(seed: string) {
|
||||
this.seed = seed
|
||||
}
|
||||
}
|
||||
129
backend/src/apis/dltConnector/model/TransactionDraft.ts
Executable file
129
backend/src/apis/dltConnector/model/TransactionDraft.ts
Executable file
@ -0,0 +1,129 @@
|
||||
// https://www.npmjs.com/package/@apollo/protobufjs
|
||||
import { AccountType } from '@dltConnector/enum/AccountType'
|
||||
import { TransactionType } from '@dltConnector/enum/TransactionType'
|
||||
|
||||
import { AccountIdentifier } from './AccountIdentifier'
|
||||
import {
|
||||
Community as DbCommunity,
|
||||
Contribution as DbContribution,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser
|
||||
} from 'database'
|
||||
import { CommunityAccountIdentifier } from './CommunityAccountIdentifier'
|
||||
import { getLogger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { IdentifierSeed } from './IdentifierSeed'
|
||||
import { CODE_VALID_DAYS_DURATION } from '@/graphql/resolver/const/const'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.dltConnector.model.TransactionDraft`)
|
||||
|
||||
export class TransactionDraft {
|
||||
user: AccountIdentifier
|
||||
// not used for simply register address
|
||||
linkedUser?: AccountIdentifier
|
||||
// not used for register address
|
||||
amount?: string
|
||||
memo?: string
|
||||
type: TransactionType
|
||||
createdAt: string
|
||||
// only for creation transaction
|
||||
targetDate?: string
|
||||
// only for deferred transaction, duration in seconds
|
||||
timeoutDuration?: number
|
||||
// only for register address
|
||||
accountType?: AccountType
|
||||
|
||||
static createRegisterAddress(user: DbUser, community: DbCommunity): TransactionDraft | null {
|
||||
if (community.hieroTopicId) {
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(community.hieroTopicId, new CommunityAccountIdentifier(user.gradidoID))
|
||||
draft.type = TransactionType.REGISTER_ADDRESS
|
||||
draft.createdAt = user.createdAt.toISOString()
|
||||
draft.accountType = AccountType.COMMUNITY_HUMAN
|
||||
return draft
|
||||
} else {
|
||||
logger.warn(`missing topicId for community ${community.id}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
static createContribution(contribution: DbContribution, createdAt: Date, signingUser: DbUser, community: DbCommunity): TransactionDraft | null {
|
||||
if (community.hieroTopicId) {
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(community.hieroTopicId, new CommunityAccountIdentifier(contribution.user.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(community.hieroTopicId, new CommunityAccountIdentifier(signingUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_CREATION
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = contribution.amount.toString()
|
||||
draft.memo = contribution.memo
|
||||
draft.targetDate = contribution.contributionDate.toISOString()
|
||||
return draft
|
||||
} else {
|
||||
logger.warn(`missing topicId for community ${community.id}`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
static createTransfer(sendingUser: DbUser, receivingUser: DbUser, amount: string, memo: string, createdAt: Date): TransactionDraft | null {
|
||||
if (!sendingUser.community || !receivingUser.community) {
|
||||
throw new Error(`missing community for user ${sendingUser.id} and/or ${receivingUser.id}`)
|
||||
}
|
||||
const senderUserTopic = sendingUser.community.hieroTopicId
|
||||
const receiverUserTopic = receivingUser.community.hieroTopicId
|
||||
if (!senderUserTopic || !receiverUserTopic) {
|
||||
throw new Error(`missing topicId for community ${sendingUser.community.id} and/or ${receivingUser.community.id}`)
|
||||
}
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new CommunityAccountIdentifier(sendingUser.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(receiverUserTopic, new CommunityAccountIdentifier(receivingUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_TRANSFER
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = amount
|
||||
draft.memo = memo
|
||||
return draft
|
||||
}
|
||||
|
||||
static createDeferredTransfer(sendingUser: DbUser, transactionLink: DbTransactionLink)
|
||||
: TransactionDraft | null {
|
||||
if (!sendingUser.community) {
|
||||
throw new Error(`missing community for user ${sendingUser.id}`)
|
||||
}
|
||||
const senderUserTopic = sendingUser.community.hieroTopicId
|
||||
if (!senderUserTopic) {
|
||||
throw new Error(`missing topicId for community ${sendingUser.community.id}`)
|
||||
}
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new CommunityAccountIdentifier(sendingUser.gradidoID))
|
||||
draft.linkedUser = new AccountIdentifier(senderUserTopic, new IdentifierSeed(transactionLink.code))
|
||||
draft.type = TransactionType.GRADIDO_DEFERRED_TRANSFER
|
||||
draft.createdAt = transactionLink.createdAt.toISOString()
|
||||
draft.amount = transactionLink.amount.toString()
|
||||
draft.memo = transactionLink.memo
|
||||
draft.timeoutDuration = CODE_VALID_DAYS_DURATION * 24 * 60 * 60
|
||||
return draft
|
||||
}
|
||||
|
||||
static redeemDeferredTransfer(transactionLink: DbTransactionLink, amount: string, createdAt: Date, recipientUser: DbUser): TransactionDraft | null {
|
||||
if (!transactionLink.user.community) {
|
||||
throw new Error(`missing community for user ${transactionLink.user.id}`)
|
||||
}
|
||||
if (!recipientUser.community) {
|
||||
throw new Error(`missing community for user ${recipientUser.id}`)
|
||||
}
|
||||
const senderUserTopic = transactionLink.user.community.hieroTopicId
|
||||
if (!senderUserTopic) {
|
||||
throw new Error(`missing topicId for community ${transactionLink.user.community.id}`)
|
||||
}
|
||||
const recipientUserTopic = recipientUser.community.hieroTopicId
|
||||
if (!recipientUserTopic) {
|
||||
throw new Error(`missing topicId for community ${recipientUser.community.id}`)
|
||||
}
|
||||
const draft = new TransactionDraft()
|
||||
draft.user = new AccountIdentifier(senderUserTopic, new IdentifierSeed(transactionLink.code))
|
||||
draft.linkedUser = new AccountIdentifier(recipientUserTopic, new CommunityAccountIdentifier(recipientUser.gradidoID))
|
||||
draft.type = TransactionType.GRADIDO_REDEEM_DEFERRED_TRANSFER
|
||||
draft.createdAt = createdAt.toISOString()
|
||||
draft.amount = amount
|
||||
return draft
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import { TransactionErrorType } from '@dltConnector/enum/TransactionErrorType'
|
||||
|
||||
export interface TransactionError {
|
||||
type: TransactionErrorType
|
||||
message: string
|
||||
name: string
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import { TransactionType } from '@dltConnector/enum/TransactionType'
|
||||
|
||||
export interface TransactionRecipe {
|
||||
id: number
|
||||
createdAt: string
|
||||
type: TransactionType
|
||||
topic: string
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import { TransactionError } from './TransactionError'
|
||||
import { TransactionRecipe } from './TransactionRecipe'
|
||||
|
||||
export interface TransactionResult {
|
||||
error?: TransactionError
|
||||
recipe?: TransactionRecipe
|
||||
succeed: boolean
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
export interface UserIdentifier {
|
||||
uuid: string
|
||||
communityUuid: string
|
||||
accountNr?: number
|
||||
}
|
||||
@ -43,6 +43,7 @@ const DLT_CONNECTOR_PORT = process.env.DLT_CONNECTOR_PORT ?? 6010
|
||||
const dltConnector = {
|
||||
DLT_CONNECTOR: process.env.DLT_CONNECTOR === 'true' || false,
|
||||
DLT_CONNECTOR_URL: process.env.DLT_CONNECTOR_URL ?? `${COMMUNITY_URL}:${DLT_CONNECTOR_PORT}`,
|
||||
DLT_GRADIDO_NODE_SERVER_HOME_FOLDER: process.env.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER ?? '~/.gradido',
|
||||
}
|
||||
|
||||
const community = {
|
||||
|
||||
@ -79,6 +79,10 @@ export const schema = Joi.object({
|
||||
.when('DLT_CONNECTOR', { is: true, then: Joi.required() })
|
||||
.description('The URL for GDT API endpoint'),
|
||||
|
||||
DLT_GRADIDO_NODE_SERVER_HOME_FOLDER: Joi.string()
|
||||
.default('~/.gradido')
|
||||
.description('The home folder for the gradido dlt node server'),
|
||||
|
||||
EMAIL: Joi.boolean()
|
||||
.default(false)
|
||||
.description('Enable or disable email functionality')
|
||||
|
||||
@ -4,4 +4,5 @@ export interface PublicCommunityInfo {
|
||||
creationDate: Date
|
||||
publicKey: string
|
||||
publicJwtKey: string
|
||||
hieroTopicId: string | null
|
||||
}
|
||||
|
||||
@ -138,6 +138,7 @@ async function writeForeignCommunity(
|
||||
com.publicKey = dbCom.publicKey
|
||||
com.publicJwtKey = pubInfo.publicJwtKey
|
||||
com.url = dbCom.endPoint
|
||||
com.hieroTopicId = pubInfo.hieroTopicId
|
||||
await DbCommunity.save(com)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { ArgsType, Field, InputType } from 'type-graphql'
|
||||
|
||||
import { Location } from '@/graphql/model/Location'
|
||||
import { isValidLocation } from '@/graphql/validator/Location'
|
||||
import { isValidHieroId } from '@/graphql/validator/HieroId'
|
||||
|
||||
@ArgsType()
|
||||
@InputType()
|
||||
@ -21,5 +22,6 @@ export class EditCommunityInput {
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsString()
|
||||
@isValidHieroId()
|
||||
hieroTopicId?: string | null
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
Contribution as DbContribution,
|
||||
Transaction as DbTransaction,
|
||||
User as DbUser,
|
||||
DltTransaction as DbDltTransaction,
|
||||
UserContact,
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
@ -59,7 +60,7 @@ import { getOpenCreations, getUserCreation, validateContribution } from './util/
|
||||
import { extractGraphQLFields } from './util/extractGraphQLFields'
|
||||
import { findContributions } from './util/findContributions'
|
||||
import { getLastTransaction } from 'database'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
import { contributionTransaction } from '@/apis/dltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.ContributionResolver`)
|
||||
@ -435,12 +436,11 @@ export class ContributionResolver {
|
||||
): Promise<boolean> {
|
||||
const logger = createLogger()
|
||||
logger.addContext('contribution', id)
|
||||
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
try {
|
||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
||||
const contribution = await DbContribution.findOne({ where: { id } })
|
||||
const contribution = await DbContribution.findOne({ where: { id }, relations: {user: {emailContact: true}} })
|
||||
if (!contribution) {
|
||||
throw new LogError('Contribution not found', id)
|
||||
}
|
||||
@ -450,18 +450,17 @@ export class ContributionResolver {
|
||||
if (contribution.contributionStatus === 'DENIED') {
|
||||
throw new LogError('Contribution already denied', id)
|
||||
}
|
||||
|
||||
const moderatorUser = getUser(context)
|
||||
if (moderatorUser.id === contribution.userId) {
|
||||
throw new LogError('Moderator can not confirm own contribution')
|
||||
}
|
||||
const user = await DbUser.findOneOrFail({
|
||||
where: { id: contribution.userId },
|
||||
withDeleted: true,
|
||||
relations: ['emailContact'],
|
||||
})
|
||||
const user = contribution.user
|
||||
if (user.deletedAt) {
|
||||
throw new LogError('Can not confirm contribution since the user was deleted')
|
||||
}
|
||||
const receivedCallDate = new Date()
|
||||
const dltTransactionPromise = contributionTransaction(contribution, moderatorUser, receivedCallDate)
|
||||
const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false)
|
||||
validateContribution(
|
||||
creations,
|
||||
@ -469,12 +468,10 @@ export class ContributionResolver {
|
||||
contribution.contributionDate,
|
||||
clientTimezoneOffset,
|
||||
)
|
||||
|
||||
const receivedCallDate = new Date()
|
||||
|
||||
const queryRunner = db.getDataSource().createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
|
||||
|
||||
const lastTransaction = await getLastTransaction(contribution.userId)
|
||||
logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined')
|
||||
|
||||
@ -491,7 +488,7 @@ export class ContributionResolver {
|
||||
}
|
||||
newBalance = newBalance.add(contribution.amount.toString())
|
||||
|
||||
const transaction = new DbTransaction()
|
||||
let transaction = new DbTransaction()
|
||||
transaction.typeId = TransactionTypeId.CREATION
|
||||
transaction.memo = contribution.memo
|
||||
transaction.userId = contribution.userId
|
||||
@ -509,7 +506,7 @@ export class ContributionResolver {
|
||||
transaction.balanceDate = receivedCallDate
|
||||
transaction.decay = decay ? decay.decay : new Decimal(0)
|
||||
transaction.decayStart = decay ? decay.start : null
|
||||
await queryRunner.manager.insert(DbTransaction, transaction)
|
||||
transaction = await queryRunner.manager.save(DbTransaction, transaction)
|
||||
|
||||
contribution.confirmedAt = receivedCallDate
|
||||
contribution.confirmedBy = moderatorUser.id
|
||||
@ -518,10 +515,7 @@ export class ContributionResolver {
|
||||
await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
|
||||
|
||||
await queryRunner.commitTransaction()
|
||||
|
||||
// trigger to send transaction via dlt-connector
|
||||
await sendTransactionsToDltConnector()
|
||||
|
||||
|
||||
logger.info('creation commited successfuly.')
|
||||
await sendContributionConfirmedEmail({
|
||||
firstName: user.firstName,
|
||||
@ -537,6 +531,17 @@ export class ContributionResolver {
|
||||
contribution.createdAt,
|
||||
),
|
||||
})
|
||||
|
||||
// update transaction id in dlt transaction tables
|
||||
// wait for finishing transaction by dlt-connector/hiero
|
||||
const dltStartTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
if(dltTransaction) {
|
||||
dltTransaction.transactionId = transaction.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
const dltEndTime = new Date()
|
||||
logger.debug(`dlt-connector contribution finished in ${dltEndTime.getTime() - dltStartTime.getTime()} ms`)
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Creation was not successful', e)
|
||||
|
||||
@ -37,11 +37,14 @@ import { TRANSACTIONS_LOCK } from 'database'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { transactionLinkCode } from './TransactionLinkResolver'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
const logErrorLogger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
|
||||
// mock semaphore to allow use fake timers
|
||||
jest.mock('database/src/util/TRANSACTIONS_LOCK')
|
||||
TRANSACTIONS_LOCK.acquire = jest.fn().mockResolvedValue(jest.fn())
|
||||
|
||||
@ -15,9 +15,13 @@ import { QueryLinkResult } from '@union/QueryLinkResult'
|
||||
import { Decay, interpretEncryptedTransferArgs, TransactionTypeId } from 'core'
|
||||
import {
|
||||
AppDatabase, Contribution as DbContribution,
|
||||
ContributionLink as DbContributionLink, FederatedCommunity as DbFederatedCommunity, Transaction as DbTransaction,
|
||||
ContributionLink as DbContributionLink,
|
||||
FederatedCommunity as DbFederatedCommunity,
|
||||
DltTransaction as DbDltTransaction,
|
||||
Transaction as DbTransaction,
|
||||
TransactionLink as DbTransactionLink,
|
||||
User as DbUser,
|
||||
findModeratorCreatingContributionLink,
|
||||
findTransactionLinkByCode,
|
||||
getHomeCommunity
|
||||
} from 'database'
|
||||
@ -36,8 +40,16 @@ import { Context, getClientTimezoneOffset, getUser } from '@/server/context'
|
||||
import { calculateBalance } from '@/util/validate'
|
||||
import { fullName } from 'core'
|
||||
import { TRANSACTION_LINK_LOCK, TRANSACTIONS_LOCK } from 'database'
|
||||
import { calculateDecay, compoundInterest, decayFormula, decode, DisburseJwtPayloadType, encode, encryptAndSign, EncryptedJWEJwtPayloadType, RedeemJwtPayloadType, verify } from 'shared'
|
||||
|
||||
import {
|
||||
calculateDecay,
|
||||
compoundInterest,
|
||||
decode,
|
||||
DisburseJwtPayloadType,
|
||||
encode,
|
||||
encryptAndSign,
|
||||
RedeemJwtPayloadType,
|
||||
verify
|
||||
} from 'shared'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { DisbursementClient as V1_0_DisbursementClient } from '@/federation/client/1_0/DisbursementClient'
|
||||
import { DisbursementClientFactory } from '@/federation/client/DisbursementClientFactory'
|
||||
@ -52,9 +64,10 @@ import {
|
||||
getCommunityByUuid,
|
||||
} from './util/communities'
|
||||
import { getUserCreation, validateContribution } from './util/creations'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
import { transactionLinkList } from './util/transactionLinkList'
|
||||
import { SignedTransferPayloadType } from 'shared'
|
||||
import { contributionTransaction, deferredTransferTransaction, redeemDeferredTransferTransaction } from '@/apis/dltConnector'
|
||||
import { CODE_VALID_DAYS_DURATION } from './const/const'
|
||||
|
||||
const createLogger = (method: string) => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionLinkResolver.${method}`)
|
||||
|
||||
@ -68,7 +81,7 @@ export const transactionLinkCode = (date: Date): string => {
|
||||
)
|
||||
}
|
||||
|
||||
const CODE_VALID_DAYS_DURATION = 14
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
|
||||
export const transactionLinkExpireDate = (date: Date): Date => {
|
||||
@ -106,11 +119,20 @@ export class TransactionLinkResolver {
|
||||
transactionLink.code = transactionLinkCode(createdDate)
|
||||
transactionLink.createdAt = createdDate
|
||||
transactionLink.validUntil = validUntil
|
||||
const dltTransactionPromise = deferredTransferTransaction(user, transactionLink)
|
||||
await DbTransactionLink.save(transactionLink).catch((e) => {
|
||||
throw new LogError('Unable to save transaction link', e)
|
||||
})
|
||||
await EVENT_TRANSACTION_LINK_CREATE(user, transactionLink, amount)
|
||||
|
||||
// wait for dlt transaction to be created
|
||||
const startTime = Date.now()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = Date.now()
|
||||
createLogger('createTransactionLink').debug(`dlt transaction created in ${endTime - startTime} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionLinkId = transactionLink.id
|
||||
await DbDltTransaction.save(dltTransaction)
|
||||
}
|
||||
return new TransactionLink(transactionLink, new User(user))
|
||||
}
|
||||
|
||||
@ -134,7 +156,6 @@ export class TransactionLinkResolver {
|
||||
user.id,
|
||||
)
|
||||
}
|
||||
|
||||
if (transactionLink.redeemedBy) {
|
||||
throw new LogError('Transaction link already redeemed', transactionLink.redeemedBy)
|
||||
}
|
||||
@ -143,7 +164,19 @@ export class TransactionLinkResolver {
|
||||
throw new LogError('Transaction link could not be deleted', e)
|
||||
})
|
||||
|
||||
transactionLink.user = user
|
||||
const dltTransactionPromise = redeemDeferredTransferTransaction(transactionLink, transactionLink.amount.toString(), transactionLink.deletedAt!, user)
|
||||
|
||||
await EVENT_TRANSACTION_LINK_DELETE(user, transactionLink)
|
||||
// wait for dlt transaction to be created
|
||||
const startTime = Date.now()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = Date.now()
|
||||
createLogger('deleteTransactionLink').debug(`dlt transaction created in ${endTime - startTime} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionLinkId = transactionLink.id
|
||||
await DbDltTransaction.save(dltTransaction)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@ -276,7 +309,7 @@ export class TransactionLinkResolver {
|
||||
throw new LogError('Contribution link has unknown cycle', contributionLink.cycle)
|
||||
}
|
||||
}
|
||||
|
||||
const moderatorPromise = findModeratorCreatingContributionLink(contributionLink)
|
||||
const creations = await getUserCreation(user.id, clientTimezoneOffset)
|
||||
methodLogger.info('open creations', creations)
|
||||
validateContribution(creations, contributionLink.amount, now, clientTimezoneOffset)
|
||||
@ -290,6 +323,12 @@ export class TransactionLinkResolver {
|
||||
contribution.contributionType = ContributionType.LINK
|
||||
contribution.contributionStatus = ContributionStatus.CONFIRMED
|
||||
|
||||
let dltTransactionPromise: Promise<DbDltTransaction | null> = Promise.resolve(null)
|
||||
const moderator = await moderatorPromise
|
||||
if (moderator) {
|
||||
dltTransactionPromise = contributionTransaction(contribution, moderator, now)
|
||||
}
|
||||
|
||||
await queryRunner.manager.insert(DbContribution, contribution)
|
||||
|
||||
const lastTransaction = await getLastTransaction(user.id)
|
||||
@ -335,6 +374,17 @@ export class TransactionLinkResolver {
|
||||
contributionLink,
|
||||
contributionLink.amount,
|
||||
)
|
||||
if (dltTransactionPromise) {
|
||||
const startTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = new Date()
|
||||
methodLogger.info(`dlt-connector transaction finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionId = transaction.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Creation from contribution link was not successful', e)
|
||||
@ -343,13 +393,11 @@ export class TransactionLinkResolver {
|
||||
}
|
||||
} finally {
|
||||
releaseLock()
|
||||
}
|
||||
// trigger to send transaction via dlt-connector
|
||||
await sendTransactionsToDltConnector()
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
const releaseLinkLock = await TRANSACTION_LINK_LOCK.acquire()
|
||||
const now = new Date()
|
||||
const releaseLinkLock = await TRANSACTION_LINK_LOCK.acquire()
|
||||
try {
|
||||
const transactionLink = await DbTransactionLink.findOne({ where: { code } })
|
||||
if (!transactionLink) {
|
||||
|
||||
@ -33,10 +33,13 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { CONFIG } from '@/config'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.server.LogError`)
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
CONFIG.EMAIL = false
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let query: ApolloServerTestClient['query']
|
||||
@ -434,50 +437,6 @@ describe('send coins', () => {
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('sendTransactionsToDltConnector', () => {
|
||||
let transaction: Transaction[]
|
||||
let dltTransactions: DltTransaction[]
|
||||
beforeAll(async () => {
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
transaction = await Transaction.find({
|
||||
where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
// and read aslong as all async created dlt-transactions are finished
|
||||
do {
|
||||
dltTransactions = await DltTransaction.find({
|
||||
where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
// order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
} while (transaction.length > dltTransactions.length)
|
||||
})
|
||||
|
||||
it('has wait till sendTransactionsToDltConnector created all dlt-transactions', () => {
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transaction[0].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transaction[1].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('send coins via gradido ID', () => {
|
||||
it('sends the coins', async () => {
|
||||
|
||||
@ -2,10 +2,13 @@ import {
|
||||
AppDatabase,
|
||||
countOpenPendingTransactions,
|
||||
Community as DbCommunity,
|
||||
DltTransaction as DbDltTransaction,
|
||||
Transaction as dbTransaction,
|
||||
TransactionLink as dbTransactionLink,
|
||||
User as dbUser,
|
||||
findUserByIdentifier
|
||||
findUserByIdentifier,
|
||||
TransactionLoggingView,
|
||||
UserLoggingView
|
||||
} from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql'
|
||||
@ -41,8 +44,8 @@ import { BalanceResolver } from './BalanceResolver'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { getCommunityName, isHomeCommunity } from './util/communities'
|
||||
import { getTransactionList } from './util/getTransactionList'
|
||||
import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector'
|
||||
import { transactionLinkSummary } from './util/transactionLinkSummary'
|
||||
import { transferTransaction, redeemDeferredTransferTransaction } from '@/apis/dltConnector'
|
||||
|
||||
const db = AppDatabase.getInstance()
|
||||
const createLogger = () => getLogger(`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.TransactionResolver`)
|
||||
@ -57,6 +60,13 @@ export const executeTransaction = async (
|
||||
): Promise<boolean> => {
|
||||
// acquire lock
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
const receivedCallDate = new Date()
|
||||
let dltTransactionPromise: Promise<DbDltTransaction | null> = Promise.resolve(null)
|
||||
if (!transactionLink) {
|
||||
dltTransactionPromise = transferTransaction(sender, recipient, amount.toString(), memo, receivedCallDate)
|
||||
} else {
|
||||
dltTransactionPromise = redeemDeferredTransferTransaction(transactionLink, amount.toString(), receivedCallDate, recipient)
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('executeTransaction', amount, memo, sender, recipient)
|
||||
@ -71,8 +81,7 @@ export const executeTransaction = async (
|
||||
throw new LogError('Sender and Recipient are the same', sender.id)
|
||||
}
|
||||
|
||||
// validate amount
|
||||
const receivedCallDate = new Date()
|
||||
// validate amount
|
||||
const sendBalance = await calculateBalance(
|
||||
sender.id,
|
||||
amount.mul(-1),
|
||||
@ -162,9 +171,15 @@ export const executeTransaction = async (
|
||||
transactionReceive,
|
||||
transactionReceive.amount,
|
||||
)
|
||||
|
||||
// trigger to send transaction via dlt-connector
|
||||
await sendTransactionsToDltConnector()
|
||||
// update dltTransaction with transactionId
|
||||
const startTime = new Date()
|
||||
const dltTransaction = await dltTransactionPromise
|
||||
const endTime = new Date()
|
||||
logger.debug(`dlt-connector transaction finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
if (dltTransaction) {
|
||||
dltTransaction.transactionId = transactionSend.id
|
||||
await dltTransaction.save()
|
||||
}
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
throw new LogError('Transaction was not successful', e)
|
||||
|
||||
@ -117,6 +117,7 @@ beforeAll(async () => {
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
CONFIG.HUMHUB_ACTIVE = false
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
|
||||
@ -104,6 +104,7 @@ import { deleteUserRole, setUserRole } from './util/modifyUserRole'
|
||||
import { sendUserToGms } from './util/sendUserToGms'
|
||||
import { syncHumhub } from './util/syncHumhub'
|
||||
import { validateAlias } from 'core'
|
||||
import { registerAddressTransaction } from '@/apis/dltConnector'
|
||||
import { updateAllDefinedAndChanged } from 'shared'
|
||||
|
||||
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
|
||||
@ -388,6 +389,7 @@ export class UserResolver {
|
||||
if (homeCom.communityUuid) {
|
||||
dbUser.communityUuid = homeCom.communityUuid
|
||||
}
|
||||
|
||||
dbUser.gradidoID = gradidoID
|
||||
dbUser.firstName = firstName
|
||||
dbUser.lastName = lastName
|
||||
@ -398,8 +400,11 @@ export class UserResolver {
|
||||
dbUser.alias = alias
|
||||
}
|
||||
dbUser.publisherId = publisherId ?? 0
|
||||
dbUser.passwordEncryptionType = PasswordEncryptionType.NO_PASSWORD
|
||||
logger.debug('new dbUser', new UserLoggingView(dbUser))
|
||||
dbUser.passwordEncryptionType = PasswordEncryptionType.NO_PASSWORD
|
||||
|
||||
if(logger.isDebugEnabled()) {
|
||||
logger.debug('new dbUser', new UserLoggingView(dbUser))
|
||||
}
|
||||
if (redeemCode) {
|
||||
if (redeemCode.match(/^CL-/)) {
|
||||
const contributionLink = await DbContributionLink.findOne({
|
||||
@ -435,7 +440,7 @@ export class UserResolver {
|
||||
|
||||
dbUser.emailContact = emailContact
|
||||
dbUser.emailId = emailContact.id
|
||||
await queryRunner.manager.save(dbUser).catch((error) => {
|
||||
dbUser = await queryRunner.manager.save(dbUser).catch((error) => {
|
||||
throw new LogError('Error while updating dbUser', error)
|
||||
})
|
||||
|
||||
@ -467,6 +472,8 @@ export class UserResolver {
|
||||
} finally {
|
||||
await queryRunner.release()
|
||||
}
|
||||
// register user into blockchain
|
||||
const dltTransactionPromise = registerAddressTransaction(dbUser, homeCom)
|
||||
logger.info('createUser() successful...')
|
||||
if (CONFIG.HUMHUB_ACTIVE) {
|
||||
let spaceId: number | null = null
|
||||
@ -503,6 +510,11 @@ export class UserResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
// wait for finishing dlt transaction
|
||||
const startTime = new Date()
|
||||
await dltTransactionPromise
|
||||
const endTime = new Date()
|
||||
logger.info(`dlt-connector register address finished in ${endTime.getTime() - startTime.getTime()} ms`)
|
||||
return new User(dbUser)
|
||||
}
|
||||
|
||||
|
||||
@ -12,3 +12,4 @@ export const MEMO_MAX_CHARS = 512
|
||||
export const MEMO_MIN_CHARS = 5
|
||||
export const DEFAULT_PAGINATION_PAGE_SIZE = 25
|
||||
export const FRONTEND_CONTRIBUTIONS_ITEM_ANCHOR_PREFIX = 'contributionListItem-'
|
||||
export const CODE_VALID_DAYS_DURATION = 14
|
||||
@ -21,9 +21,14 @@ import {
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { CONFIG } from '@/config'
|
||||
import { TRANSACTIONS_LOCK } from 'database'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
CONFIG.EMAIL = false
|
||||
|
||||
let mutate: ApolloServerTestClient['mutate']
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
@ -44,7 +49,43 @@ afterAll(async () => {
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
type RunOrder = { [key: number]: { start: number, end: number } }
|
||||
async function fakeWork(runOrder: RunOrder, index: number) {
|
||||
const releaseLock = await TRANSACTIONS_LOCK.acquire()
|
||||
const startDate = new Date()
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.random() * 50))
|
||||
const endDate = new Date()
|
||||
runOrder[index] = { start: startDate.getTime(), end: endDate.getTime() }
|
||||
releaseLock()
|
||||
}
|
||||
|
||||
describe('semaphore', () => {
|
||||
it("didn't should run in parallel", async () => {
|
||||
const runOrder: RunOrder = {}
|
||||
await Promise.all([
|
||||
fakeWork(runOrder, 1),
|
||||
fakeWork(runOrder, 2),
|
||||
fakeWork(runOrder, 3),
|
||||
fakeWork(runOrder, 4),
|
||||
fakeWork(runOrder, 5),
|
||||
])
|
||||
expect(runOrder[1].start).toBeLessThan(runOrder[1].end)
|
||||
expect(runOrder[1].start).toBeLessThan(runOrder[2].start)
|
||||
expect(runOrder[2].start).toBeLessThan(runOrder[2].end)
|
||||
expect(runOrder[2].start).toBeLessThan(runOrder[3].start)
|
||||
expect(runOrder[3].start).toBeLessThan(runOrder[3].end)
|
||||
expect(runOrder[3].start).toBeLessThan(runOrder[4].start)
|
||||
expect(runOrder[4].start).toBeLessThan(runOrder[4].end)
|
||||
expect(runOrder[4].start).toBeLessThan(runOrder[5].start)
|
||||
expect(runOrder[5].start).toBeLessThan(runOrder[5].end)
|
||||
expect(runOrder[1].end).toBeLessThan(runOrder[2].end)
|
||||
expect(runOrder[2].end).toBeLessThan(runOrder[3].end)
|
||||
expect(runOrder[3].end).toBeLessThan(runOrder[4].end)
|
||||
expect(runOrder[4].end).toBeLessThan(runOrder[5].end)
|
||||
})
|
||||
})
|
||||
|
||||
describe('semaphore fullstack', () => {
|
||||
let contributionLinkCode = ''
|
||||
let bobsTransactionLinkCode = ''
|
||||
let bibisTransactionLinkCode = ''
|
||||
|
||||
@ -1,799 +0,0 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Community, DltTransaction, Transaction } from 'database'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
// import { GraphQLClient } from 'graphql-request'
|
||||
// import { Response } from 'graphql-request/dist/types'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { Response } from 'graphql-request/dist/types'
|
||||
import { DataSource } from 'typeorm'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { i18n as localization } from '@test/testSetup'
|
||||
|
||||
import { CONFIG } from '@/config'
|
||||
import { TransactionTypeId } from 'core'
|
||||
import { creations } from '@/seeds/creation'
|
||||
import { creationFactory } from '@/seeds/factory/creation'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||
import { getLogger } from 'config-schema/test/testSetup'
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
|
||||
import { sendTransactionsToDltConnector } from './sendTransactionsToDltConnector'
|
||||
|
||||
jest.mock('@/password/EncryptorUtils')
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`,
|
||||
)
|
||||
|
||||
/*
|
||||
// Mock the GraphQLClient
|
||||
jest.mock('graphql-request', () => {
|
||||
const originalModule = jest.requireActual('graphql-request')
|
||||
|
||||
let testCursor = 0
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
...originalModule,
|
||||
GraphQLClient: jest.fn().mockImplementation((url: string) => {
|
||||
if (url === 'invalid') {
|
||||
throw new Error('invalid url')
|
||||
}
|
||||
return {
|
||||
// why not using mockResolvedValueOnce or mockReturnValueOnce?
|
||||
// I have tried, but it didn't work and return every time the first value
|
||||
request: jest.fn().mockImplementation(() => {
|
||||
testCursor++
|
||||
if (testCursor === 4) {
|
||||
return Promise.resolve(
|
||||
// invalid, is 33 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516212A',
|
||||
},
|
||||
},
|
||||
)
|
||||
} else if (testCursor === 5) {
|
||||
throw Error('Connection error')
|
||||
} else {
|
||||
return Promise.resolve(
|
||||
// valid, is 32 Bytes long as binary
|
||||
{
|
||||
transmitTransaction: {
|
||||
dltTransactionIdHex:
|
||||
'723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc51621',
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
let mutate: ApolloServerTestClient['mutate'],
|
||||
query: ApolloServerTestClient['query'],
|
||||
con: Connection
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: Connection
|
||||
}
|
||||
*/
|
||||
|
||||
async function createHomeCommunity(): Promise<Community> {
|
||||
const homeCommunity = Community.create()
|
||||
homeCommunity.foreign = false
|
||||
homeCommunity.communityUuid = uuidv4()
|
||||
homeCommunity.url = 'localhost'
|
||||
homeCommunity.publicKey = Buffer.from('0x6e6a6c6d6feffe', 'hex')
|
||||
await Community.save(homeCommunity)
|
||||
return homeCommunity
|
||||
}
|
||||
|
||||
async function createTxCREATION1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(100)
|
||||
tx.balanceDate = new Date('01.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION1'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION1.userGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txCREATION 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('01.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('01.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxCREATION2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(200)
|
||||
tx.balanceDate = new Date('02.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION2'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION2.userGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txCREATION 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('02.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('02.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxCREATION3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(1000)
|
||||
tx.balance = new Decimal(300)
|
||||
tx.balanceDate = new Date('03.01.2023 00:00:00')
|
||||
tx.memo = 'txCREATION3'
|
||||
tx.typeId = TransactionTypeId.CREATION
|
||||
tx.userGradidoID = 'txCREATION3.userGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txCREATION 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('03.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('03.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxSend1ToReceive2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(100)
|
||||
tx.balance = new Decimal(1000)
|
||||
tx.balanceDate = new Date('11.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND1 to txRECEIVE2'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND1.userGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txSEND 1'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE2.linkedUserGradidoID'
|
||||
tx.linkedUserId = 2
|
||||
tx.linkedUserName = 'txRECEIVE 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('11.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('11.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive2FromSend1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(100)
|
||||
tx.balance = new Decimal(1300)
|
||||
tx.balanceDate = new Date('11.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND1 to txRECEIVE2'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE2.linkedUserGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txRECEIVE 2'
|
||||
tx.linkedUserGradidoID = 'txSEND1.userGradidoID'
|
||||
tx.linkedUserId = 1
|
||||
tx.linkedUserName = 'txSEND 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('11.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('11.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
/*
|
||||
async function createTxSend2ToReceive3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(200)
|
||||
tx.balance = new Decimal(1100)
|
||||
tx.balanceDate = new Date('23.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND2 to txRECEIVE3'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND2.userGradidoID'
|
||||
tx.userId = 2
|
||||
tx.userName = 'txSEND 2'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE3.linkedUserGradidoID'
|
||||
tx.linkedUserId = 3
|
||||
tx.linkedUserName = 'txRECEIVE 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('23.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a2'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('23.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive3FromSend2(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(200)
|
||||
tx.balance = new Decimal(1500)
|
||||
tx.balanceDate = new Date('23.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND2 to txRECEIVE3'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE3.linkedUserGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txRECEIVE 3'
|
||||
tx.linkedUserGradidoID = 'txSEND2.userGradidoID'
|
||||
tx.linkedUserId = 2
|
||||
tx.linkedUserName = 'txSEND 2'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('23.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('23.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxSend3ToReceive1(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(300)
|
||||
tx.balance = new Decimal(1200)
|
||||
tx.balanceDate = new Date('31.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND3 to txRECEIVE1'
|
||||
tx.typeId = TransactionTypeId.SEND
|
||||
tx.userGradidoID = 'txSEND3.userGradidoID'
|
||||
tx.userId = 3
|
||||
tx.userName = 'txSEND 3'
|
||||
tx.linkedUserGradidoID = 'txRECEIVE1.linkedUserGradidoID'
|
||||
tx.linkedUserId = 1
|
||||
tx.linkedUserName = 'txRECEIVE 1'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('31.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516a3'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('31.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
async function createTxReceive1FromSend3(verified: boolean): Promise<Transaction> {
|
||||
let tx = Transaction.create()
|
||||
tx.amount = new Decimal(300)
|
||||
tx.balance = new Decimal(1300)
|
||||
tx.balanceDate = new Date('31.01.2023 00:00:00')
|
||||
tx.memo = 'txSEND3 to txRECEIVE1'
|
||||
tx.typeId = TransactionTypeId.RECEIVE
|
||||
tx.userGradidoID = 'txRECEIVE1.linkedUserGradidoID'
|
||||
tx.userId = 1
|
||||
tx.userName = 'txRECEIVE 1'
|
||||
tx.linkedUserGradidoID = 'txSEND3.userGradidoID'
|
||||
tx.linkedUserId = 3
|
||||
tx.linkedUserName = 'txSEND 3'
|
||||
tx = await Transaction.save(tx)
|
||||
|
||||
if (verified) {
|
||||
const dlttx = DltTransaction.create()
|
||||
dlttx.createdAt = new Date('31.01.2023 00:00:10')
|
||||
dlttx.messageId = '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516b1'
|
||||
dlttx.transactionId = tx.id
|
||||
dlttx.verified = true
|
||||
dlttx.verifiedAt = new Date('31.01.2023 00:01:10')
|
||||
await DltTransaction.save(dlttx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
*/
|
||||
|
||||
let con: DataSource
|
||||
let testEnv: {
|
||||
mutate: ApolloServerTestClient['mutate']
|
||||
query: ApolloServerTestClient['query']
|
||||
con: DataSource
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment(logger, localization)
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.destroy()
|
||||
})
|
||||
|
||||
describe('create and send Transactions to DltConnector', () => {
|
||||
let txCREATION1: Transaction
|
||||
let txCREATION2: Transaction
|
||||
let txCREATION3: Transaction
|
||||
let txSEND1to2: Transaction
|
||||
let txRECEIVE2From1: Transaction
|
||||
// let txSEND2To3: Transaction
|
||||
// let txRECEIVE3From2: Transaction
|
||||
// let txSEND3To1: Transaction
|
||||
// let txRECEIVE1From3: Transaction
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanDB()
|
||||
})
|
||||
|
||||
describe('with 3 creations but inactive dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
txCREATION1 = await createTxCREATION1(false)
|
||||
txCREATION2 = await createTxCREATION2(false)
|
||||
txCREATION3 = await createTxCREATION3(false)
|
||||
await createHomeCommunity()
|
||||
|
||||
CONFIG.DLT_CONNECTOR = false
|
||||
await sendTransactionsToDltConnector()
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[0].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[1].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[2].id,
|
||||
messageId: null,
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
|
||||
expect(logger.info).nthCalledWith(2, 'sending to DltConnector currently not configured...')
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 3 creations and active dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, raeuberHotzenplotz)
|
||||
await userFactory(testEnv, bobBaumeister)
|
||||
let count = 0
|
||||
for (const creation of creations) {
|
||||
await creationFactory(testEnv, creation)
|
||||
count++
|
||||
// we need only 3 for testing
|
||||
if (count >= 3) {
|
||||
break
|
||||
}
|
||||
}
|
||||
await createHomeCommunity()
|
||||
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
|
||||
await sendTransactionsToDltConnector()
|
||||
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[0].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[1].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: transactions[2].id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 3 verified creations, 1 sendCoins and active dlt-connector', () => {
|
||||
it('found 3 dlt-transactions', async () => {
|
||||
txCREATION1 = await createTxCREATION1(true)
|
||||
txCREATION2 = await createTxCREATION2(true)
|
||||
txCREATION3 = await createTxCREATION3(true)
|
||||
await createHomeCommunity()
|
||||
|
||||
txSEND1to2 = await createTxSend1ToReceive2(false)
|
||||
txRECEIVE2From1 = await createTxReceive2FromSend1(false)
|
||||
|
||||
/*
|
||||
txSEND2To3 = await createTxSend2ToReceive3()
|
||||
txRECEIVE3From2 = await createTxReceive3FromSend2()
|
||||
txSEND3To1 = await createTxSend3ToReceive1()
|
||||
txRECEIVE1From3 = await createTxReceive1FromSend3()
|
||||
*/
|
||||
|
||||
CONFIG.DLT_CONNECTOR = true
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
return {
|
||||
data: {
|
||||
sendTransaction: { succeed: true },
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
|
||||
await sendTransactionsToDltConnector()
|
||||
|
||||
expect(logger.info).toBeCalledWith('sendTransactionsToDltConnector...')
|
||||
|
||||
// Find the previous created transactions of sendCoin mutation
|
||||
/*
|
||||
const transactions = await Transaction.find({
|
||||
// where: { memo: 'unrepeatable memo' },
|
||||
order: { balanceDate: 'ASC', id: 'ASC' },
|
||||
})
|
||||
*/
|
||||
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
// where: { transactionId: In([transaction[0].id, transaction[1].id]) },
|
||||
// relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
expect(dltTransactions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION1.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c1',
|
||||
verified: true,
|
||||
createdAt: new Date('01.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('01.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION2.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c2',
|
||||
verified: true,
|
||||
createdAt: new Date('02.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('02.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txCREATION3.id,
|
||||
messageId: '723e3fab62c5d3e2f62fd72ba4e622bcd53eff35262e3f3526327fe41bc516c3',
|
||||
verified: true,
|
||||
createdAt: new Date('03.01.2023 00:00:10'),
|
||||
verifiedAt: new Date('03.01.2023 00:01:10'),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txSEND1to2.id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
transactionId: txRECEIVE2From1.id,
|
||||
messageId: 'sended',
|
||||
verified: false,
|
||||
createdAt: expect.any(Date),
|
||||
verifiedAt: null,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
/*
|
||||
describe('with one Community of api 1_0 and not matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: 'somePubKey',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables1)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs not matching publicKeys', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Federation: received not matching publicKey:',
|
||||
'somePubKey',
|
||||
expect.stringMatching('11111111111111111111111111111111'),
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with one Community of api 1_0 and matching pubKey', () => {
|
||||
beforeEach(async () => {
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: '11111111111111111111111111111111',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables1 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables1)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
|
||||
it('logs one community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 1 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs community pubKey verified', () => {
|
||||
expect(logger.info).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'Federation: verified community with',
|
||||
'http//localhost:5001/api/',
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with two Communities of api 1_0 and 1_1', () => {
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
jest.spyOn(GraphQLClient.prototype, 'rawRequest').mockImplementation(async () => {
|
||||
|
||||
return {
|
||||
data: {
|
||||
getPublicKey: {
|
||||
publicKey: '11111111111111111111111111111111',
|
||||
},
|
||||
},
|
||||
} as Response<unknown>
|
||||
})
|
||||
const variables2 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '1_1',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables2)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
it('logs two communities found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 2 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
})
|
||||
describe('with three Communities of api 1_0, 1_1 and 2_0', () => {
|
||||
let dbCom: DbFederatedCommunity
|
||||
beforeEach(async () => {
|
||||
const variables3 = {
|
||||
publicKey: Buffer.from('11111111111111111111111111111111'),
|
||||
apiVersion: '2_0',
|
||||
endPoint: 'http//localhost:5001/api/',
|
||||
lastAnnouncedAt: new Date(),
|
||||
}
|
||||
await DbFederatedCommunity.createQueryBuilder()
|
||||
.insert()
|
||||
.into(DbFederatedCommunity)
|
||||
.values(variables3)
|
||||
.orUpdate({
|
||||
|
||||
conflict_target: ['id', 'publicKey', 'apiVersion'],
|
||||
overwrite: ['end_point', 'last_announced_at'],
|
||||
})
|
||||
.execute()
|
||||
dbCom = await DbFederatedCommunity.findOneOrFail({
|
||||
where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion },
|
||||
})
|
||||
await DbFederatedCommunity.update({}, { verifiedAt: null })
|
||||
jest.clearAllMocks()
|
||||
// await validateCommunities()
|
||||
})
|
||||
it('logs three community found', () => {
|
||||
expect(logger.debug).toBeCalledWith(`Federation: found 3 dbCommunities`)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_0 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_0/',
|
||||
)
|
||||
})
|
||||
it('logs requestGetPublicKey for community api 1_1 ', () => {
|
||||
expect(logger.info).toBeCalledWith(
|
||||
'Federation: getPublicKey from endpoint',
|
||||
'http//localhost:5001/api/1_1/',
|
||||
)
|
||||
})
|
||||
it('logs unsupported api for community with api 2_0 ', () => {
|
||||
expect(logger.warn).toBeCalledWith(
|
||||
'Federation: dbCom with unsupported apiVersion',
|
||||
dbCom.endPoint,
|
||||
'2_0',
|
||||
)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
})
|
||||
@ -1,85 +0,0 @@
|
||||
import { DltTransaction, Transaction } from 'database'
|
||||
import { IsNull } from 'typeorm'
|
||||
|
||||
import { DltConnectorClient } from '@dltConnector/DltConnectorClient'
|
||||
|
||||
import { LOG4JS_BASE_CATEGORY_NAME } from '@/config/const'
|
||||
import { Monitor, MonitorNames } from '@/util/Monitor'
|
||||
import { getLogger } from 'log4js'
|
||||
|
||||
const logger = getLogger(
|
||||
`${LOG4JS_BASE_CATEGORY_NAME}.graphql.resolver.util.sendTransactionsToDltConnector`,
|
||||
)
|
||||
|
||||
export async function sendTransactionsToDltConnector(): Promise<void> {
|
||||
logger.info('sendTransactionsToDltConnector...')
|
||||
// check if this logic is still occupied, no concurrecy allowed
|
||||
if (!Monitor.isLocked(MonitorNames.SEND_DLT_TRANSACTIONS)) {
|
||||
// mark this block for occuption to prevent concurrency
|
||||
Monitor.lockIt(MonitorNames.SEND_DLT_TRANSACTIONS)
|
||||
|
||||
try {
|
||||
await createDltTransactions()
|
||||
const dltConnector = DltConnectorClient.getInstance()
|
||||
if (dltConnector) {
|
||||
logger.debug('with sending to DltConnector...')
|
||||
const dltTransactions = await DltTransaction.find({
|
||||
where: { messageId: IsNull() },
|
||||
relations: ['transaction'],
|
||||
order: { createdAt: 'ASC', id: 'ASC' },
|
||||
})
|
||||
|
||||
for (const dltTx of dltTransactions) {
|
||||
if (!dltTx.transaction) {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const result = await dltConnector.transmitTransaction(dltTx.transaction)
|
||||
// message id isn't known at this point of time, because transaction will not direct sended to iota,
|
||||
// it will first go to db and then sended, if no transaction is in db before
|
||||
if (result) {
|
||||
dltTx.messageId = 'sended'
|
||||
await DltTransaction.save(dltTx)
|
||||
logger.info(`store messageId=${dltTx.messageId} in dltTx=${dltTx.id}`)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(
|
||||
`error while sending to dlt-connector or writing messageId of dltTx=${dltTx.id}`,
|
||||
e,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.info('sending to DltConnector currently not configured...')
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('error on sending transactions to dlt-connector.', e)
|
||||
} finally {
|
||||
// releae Monitor occupation
|
||||
Monitor.releaseIt(MonitorNames.SEND_DLT_TRANSACTIONS)
|
||||
}
|
||||
} else {
|
||||
logger.info('sendTransactionsToDltConnector currently locked by monitor...')
|
||||
}
|
||||
}
|
||||
|
||||
async function createDltTransactions(): Promise<void> {
|
||||
const dltqb = DltTransaction.createQueryBuilder().select('transactions_id')
|
||||
const newTransactions: Transaction[] = await Transaction.createQueryBuilder()
|
||||
.select('id')
|
||||
.addSelect('balance_date')
|
||||
.where('id NOT IN (' + dltqb.getSql() + ')')
|
||||
|
||||
.orderBy({ balance_date: 'ASC', id: 'ASC' })
|
||||
.getRawMany()
|
||||
|
||||
const dltTxArray: DltTransaction[] = []
|
||||
let idx = 0
|
||||
while (newTransactions.length > dltTxArray.length) {
|
||||
// timing problems with for(let idx = 0; idx < newTransactions.length; idx++) {
|
||||
const dltTx = DltTransaction.create()
|
||||
dltTx.transactionId = newTransactions[idx++].id
|
||||
await DltTransaction.save(dltTx)
|
||||
dltTxArray.push(dltTx)
|
||||
}
|
||||
}
|
||||
23
backend/src/graphql/validator/HieroId.ts
Normal file
23
backend/src/graphql/validator/HieroId.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { ValidationArguments, ValidationOptions, registerDecorator } from 'class-validator'
|
||||
|
||||
export function isValidHieroId(validationOptions?: ValidationOptions) {
|
||||
return function (object: Object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isValidHieroId',
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate(value: string) {
|
||||
if (value.match(/[0-9]*\.[0-9]*\.[0-9]*/)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `${propertyName} must be a valid HieroId (0.0.2121), ${args.property}`
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
352
bun.lock
352
bun.lock
@ -18,7 +18,7 @@
|
||||
},
|
||||
"admin": {
|
||||
"name": "admin",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
@ -88,7 +88,7 @@
|
||||
},
|
||||
"backend": {
|
||||
"name": "backend",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"email-templates": "^10.0.1",
|
||||
@ -166,7 +166,7 @@
|
||||
},
|
||||
"config-schema": {
|
||||
"name": "config-schema",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.2",
|
||||
"joi": "17.13.3",
|
||||
@ -184,7 +184,7 @@
|
||||
},
|
||||
"core": {
|
||||
"name": "core",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"database": "*",
|
||||
"esbuild": "^0.25.2",
|
||||
@ -213,7 +213,7 @@
|
||||
},
|
||||
"database": {
|
||||
"name": "database",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"@types/uuid": "^8.3.4",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -255,7 +255,7 @@
|
||||
},
|
||||
"dht-node": {
|
||||
"name": "dht-node",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"dht-rpc": "6.18.1",
|
||||
@ -292,7 +292,7 @@
|
||||
},
|
||||
"federation": {
|
||||
"name": "federation",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"sodium-native": "^3.4.1",
|
||||
@ -348,7 +348,7 @@
|
||||
},
|
||||
"frontend": {
|
||||
"name": "frontend",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"@morev/vue-transitions": "^3.0.2",
|
||||
"@types/leaflet": "^1.9.12",
|
||||
@ -444,7 +444,7 @@
|
||||
},
|
||||
"shared": {
|
||||
"name": "shared",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"dependencies": {
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"esbuild": "^0.25.2",
|
||||
@ -492,51 +492,51 @@
|
||||
|
||||
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
|
||||
|
||||
"@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.908.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.908.0", "@aws-sdk/credential-provider-node": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.907.0", "@aws-sdk/util-user-agent-node": "3.908.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.15.0", "@smithy/fetch-http-handler": "^5.3.1", "@smithy/hash-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", "@smithy/middleware-endpoint": "^4.3.1", "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.0", "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-e4bglczZvoDLmMeAgr0lou5QKCIOGfdCnfXp9jkEhkL6JLKNYvkmrRZpCrQiBwm/4j4H88oRPgLYk+sGQKFPlw=="],
|
||||
"@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.913.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/credential-provider-node": "3.913.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-jUF1mN+webeAgkNXS/tl6KpJyUbsAWxQGsQgsWoHwaNCSnxMDBEyPmgBnzbqf2CrybIa7zmzaqCO0z6FgKeZRg=="],
|
||||
|
||||
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.908.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.907.0", "@aws-sdk/util-user-agent-node": "3.908.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.15.0", "@smithy/fetch-http-handler": "^5.3.1", "@smithy/hash-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", "@smithy/middleware-endpoint": "^4.3.1", "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.0", "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PseFMWvtac+Q+zaY9DMISE+2+glNh0ROJ1yR4gMzeafNHSwkdYu4qcgxLWIOnIodGydBv/tQ6nzHPzExXnUUgw=="],
|
||||
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.911.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-N9QAeMvN3D1ZyKXkQp4aUgC4wUMuA5E1HuVCkajc0bq1pnH4PIke36YlrDGGREqPlyLFrXCkws2gbL5p23vtlg=="],
|
||||
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.908.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@aws-sdk/xml-builder": "3.901.0", "@smithy/core": "^3.15.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/signature-v4": "^5.3.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-okl6FC2cQT1Oidvmnmvyp/IEvqENBagKO0ww4YV5UtBkf0VlhAymCWkZqhovtklsqgq0otag2VRPAgnrMt6nVQ=="],
|
||||
"@aws-sdk/core": ["@aws-sdk/core@3.911.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@aws-sdk/xml-builder": "3.911.0", "@smithy/core": "^3.16.1", "@smithy/node-config-provider": "^4.3.2", "@smithy/property-provider": "^4.2.2", "@smithy/protocol-http": "^5.3.2", "@smithy/signature-v4": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k4QG9A+UCq/qlDJFmjozo6R0eXXfe++/KnCDMmajehIE9kh+b/5DqlGvAmbl9w4e92LOtrY6/DN3mIX1xs4sXw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-FK2YuxoI5CxUflPOIMbVAwDbi6Xvu+2sXopXLmrHc2PfI39M3vmjEoQwYCP8WuQSRb+TbAP3xAkxHjFSBFR35w=="],
|
||||
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-6FWRwWn3LUZzLhqBXB+TPMW2ijCWUqGICSw8bVakEdODrvbiv1RT/MVUayzFwz/ek6e6NKZn6DbSWzx07N9Hjw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/fetch-http-handler": "^5.3.1", "@smithy/node-http-handler": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" } }, "sha512-eLbz0geVW9EykujQNnYfR35Of8MreI6pau5K6XDFDUSWO9GF8wqH7CQwbXpXHBlCTHtq4QSLxzorD8U5CROhUw=="],
|
||||
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/node-http-handler": "^4.4.1", "@smithy/property-provider": "^4.2.2", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/util-stream": "^4.5.2", "tslib": "^2.6.2" } }, "sha512-xUlwKmIUW2fWP/eM3nF5u4CyLtOtyohlhGJ5jdsJokr3MrQ7w0tDITO43C9IhCn+28D5UbaiWnKw5ntkw7aVfA=="],
|
||||
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/credential-provider-env": "3.908.0", "@aws-sdk/credential-provider-http": "3.908.0", "@aws-sdk/credential-provider-process": "3.908.0", "@aws-sdk/credential-provider-sso": "3.908.0", "@aws-sdk/credential-provider-web-identity": "3.908.0", "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-7Cgnv5wabgFtsgr+Uc/76EfPNGyxmbG8aICn3g3D3iJlcO4uuOZI8a77i0afoDdchZrTC6TG6UusS/NAW6zEoQ=="],
|
||||
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.913.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/credential-provider-env": "3.911.0", "@aws-sdk/credential-provider-http": "3.911.0", "@aws-sdk/credential-provider-process": "3.911.0", "@aws-sdk/credential-provider-sso": "3.911.0", "@aws-sdk/credential-provider-web-identity": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/credential-provider-imds": "^4.2.2", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-iR4c4NQ1OSRKQi0SxzpwD+wP1fCy+QNKtEyCajuVlD0pvmoIHdrm5THK9e+2/7/SsQDRhOXHJfLGxHapD74WJw=="],
|
||||
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.908.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.908.0", "@aws-sdk/credential-provider-http": "3.908.0", "@aws-sdk/credential-provider-ini": "3.908.0", "@aws-sdk/credential-provider-process": "3.908.0", "@aws-sdk/credential-provider-sso": "3.908.0", "@aws-sdk/credential-provider-web-identity": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-8OKbykpGw5bdfF/pLTf8YfUi1Kl8o1CTjBqWQTsLOkE3Ho3hsp1eQx8Cz4ttrpv0919kb+lox62DgmAOEmTr1w=="],
|
||||
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.913.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.911.0", "@aws-sdk/credential-provider-http": "3.911.0", "@aws-sdk/credential-provider-ini": "3.913.0", "@aws-sdk/credential-provider-process": "3.911.0", "@aws-sdk/credential-provider-sso": "3.911.0", "@aws-sdk/credential-provider-web-identity": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/credential-provider-imds": "^4.2.2", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-HQPLkKDxS83Q/nZKqg9bq4igWzYQeOMqhpx5LYs4u1GwsKeCsYrrfz12Iu4IHNWPp9EnGLcmdfbfYuqZGrsaSQ=="],
|
||||
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-sWnbkGjDPBi6sODUzrAh5BCDpnPw0wpK8UC/hWI13Q8KGfyatAmCBfr+9OeO3+xBHa8N5AskMncr7C4qS846yQ=="],
|
||||
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-mKshhV5jRQffZjbK9x7bs+uC2IsYKfpzYaBamFsEov3xtARCpOiKaIlM8gYKFEbHT2M+1R3rYYlhhl9ndVWS2g=="],
|
||||
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.908.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.908.0", "@aws-sdk/core": "3.908.0", "@aws-sdk/token-providers": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-WV/aOzuS6ZZhrkPty6TJ3ZG24iS8NXP0m3GuTVuZ5tKi9Guss31/PJ1CrKPRCYGm15CsIjf+mrUxVnNYv9ap5g=="],
|
||||
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.911.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.911.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/token-providers": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-JAxd4uWe0Zc9tk6+N0cVxe9XtJVcOx6Ms0k933ZU9QbuRMH6xti/wnZxp/IvGIWIDzf5fhqiGyw5MSyDeI5b1w=="],
|
||||
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-9xWrFn6nWlF5KlV4XYW+7E6F33S3wUUEGRZ/+pgDhkIZd527ycT2nPG2dZ3fWUZMlRmzijP20QIJDqEbbGWe1Q=="],
|
||||
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-urIbXWWG+cm54RwwTFQuRwPH0WPsMFSDF2/H9qO2J2fKoHRURuyblFCyYG3aVKZGvFBhOizJYexf5+5w3CJKBw=="],
|
||||
|
||||
"@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.901.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-yWX7GvRmqBtbNnUW7qbre3GvZmyYwU0WHefpZzDTYDoNgatuYq6LgUIQ+z5C04/kCRoFkAFrHag8a3BXqFzq5A=="],
|
||||
"@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-F9Lqeu80/aTM6S/izZ8RtwSmjfhWjIuxX61LX+/9mxJyEkgaECRxv0chsLQsLHJumkGnXRy/eIyMLBhcTPF5vg=="],
|
||||
|
||||
"@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.901.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-UoHebjE7el/tfRo8/CQTj91oNUm+5Heus5/a4ECdmWaSCHCS/hXTsU3PTTHAY67oAQR8wBLFPfp3mMvXjB+L2A=="],
|
||||
"@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-3LJyyfs1USvRuRDla1pGlzGRtXJBXD1zC9F+eE9Iz/V5nkmhyv52A017CvKWmYoR0DM9dzjLyPOI0BSSppEaTw=="],
|
||||
|
||||
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.901.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@aws/lambda-invoke-store": "^0.0.1", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-Wd2t8qa/4OL0v/oDpCHHYkgsXJr8/ttCxrvCKAt0H1zZe2LlRhY9gpDVKqdertfHrHDj786fOvEQA28G1L75Dg=="],
|
||||
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@aws/lambda-invoke-store": "^0.0.1", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-m/oLz0EoCy+WoIVBnXRXJ4AtGpdl0kPE7U+VH9TsuUzHgxY1Re/176Q1HWLBRVlz4gr++lNsgsMWEC+VnAwMpw=="],
|
||||
|
||||
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@smithy/core": "^3.15.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-R0ePEOku72EvyJWy/D0Z5f/Ifpfxa0U9gySO3stpNhOox87XhsILpcIsCHPy0OHz1a7cMoZsF6rMKSzDeCnogQ=="],
|
||||
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@smithy/core": "^3.16.1", "@smithy/protocol-http": "^5.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-rY3LvGvgY/UI0nmt5f4DRzjEh8135A2TeHcva1bgOmVfOI4vkkGfA20sNRqerOkSO6hPbkxJapO50UJHFzmmyA=="],
|
||||
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.908.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.908.0", "@aws-sdk/middleware-host-header": "3.901.0", "@aws-sdk/middleware-logger": "3.901.0", "@aws-sdk/middleware-recursion-detection": "3.901.0", "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/region-config-resolver": "3.901.0", "@aws-sdk/types": "3.901.0", "@aws-sdk/util-endpoints": "3.901.0", "@aws-sdk/util-user-agent-browser": "3.907.0", "@aws-sdk/util-user-agent-node": "3.908.0", "@smithy/config-resolver": "^4.3.0", "@smithy/core": "^3.15.0", "@smithy/fetch-http-handler": "^5.3.1", "@smithy/hash-node": "^4.2.0", "@smithy/invalid-dependency": "^4.2.0", "@smithy/middleware-content-length": "^4.2.0", "@smithy/middleware-endpoint": "^4.3.1", "@smithy/middleware-retry": "^4.4.1", "@smithy/middleware-serde": "^4.2.0", "@smithy/middleware-stack": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/node-http-handler": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.0", "@smithy/util-defaults-mode-node": "^4.2.1", "@smithy/util-endpoints": "^3.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZxDYrfxOKXNFHLyvJtT96TJ0p4brZOhwRE4csRXrezEVUN+pNgxuem95YvMALPVhlVqON2CTzr8BX+CcBKvX9Q=="],
|
||||
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.911.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.911.0", "@aws-sdk/middleware-host-header": "3.910.0", "@aws-sdk/middleware-logger": "3.910.0", "@aws-sdk/middleware-recursion-detection": "3.910.0", "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/region-config-resolver": "3.910.0", "@aws-sdk/types": "3.910.0", "@aws-sdk/util-endpoints": "3.910.0", "@aws-sdk/util-user-agent-browser": "3.910.0", "@aws-sdk/util-user-agent-node": "3.911.0", "@smithy/config-resolver": "^4.3.2", "@smithy/core": "^3.16.1", "@smithy/fetch-http-handler": "^5.3.3", "@smithy/hash-node": "^4.2.2", "@smithy/invalid-dependency": "^4.2.2", "@smithy/middleware-content-length": "^4.2.2", "@smithy/middleware-endpoint": "^4.3.3", "@smithy/middleware-retry": "^4.4.3", "@smithy/middleware-serde": "^4.2.2", "@smithy/middleware-stack": "^4.2.2", "@smithy/node-config-provider": "^4.3.2", "@smithy/node-http-handler": "^4.4.1", "@smithy/protocol-http": "^5.3.2", "@smithy/smithy-client": "^4.8.1", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.2", "@smithy/util-defaults-mode-node": "^4.2.3", "@smithy/util-endpoints": "^3.2.2", "@smithy/util-middleware": "^4.2.2", "@smithy/util-retry": "^4.2.2", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-lp/sXbdX/S0EYaMYPVKga0omjIUbNNdFi9IJITgKZkLC6CzspihIoHd5GIdl4esMJevtTQQfkVncXTFkf/a4YA=="],
|
||||
|
||||
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.901.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7F0N888qVLHo4CSQOsnkZ4QAp8uHLKJ4v3u09Ly5k4AEStrSlFpckTPyUx6elwGL+fxGjNE2aakK8vEgzzCV0A=="],
|
||||
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/node-config-provider": "^4.3.2", "@smithy/types": "^4.7.1", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-gzQAkuHI3xyG6toYnH/pju+kc190XmvnB7X84vtN57GjgdQJICt9So/BD0U6h+eSfk9VBnafkVrAzBzWMEFZVw=="],
|
||||
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.908.0", "", { "dependencies": { "@aws-sdk/core": "3.908.0", "@aws-sdk/nested-clients": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-4SosHWRQ8hj1X2yDenCYHParcCjHcd7S+Mdb/lelwF0JBFCNC+dNCI9ws3cP/dFdZO/AIhJQGUBzEQtieloixw=="],
|
||||
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.911.0", "", { "dependencies": { "@aws-sdk/core": "3.911.0", "@aws-sdk/nested-clients": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/property-provider": "^4.2.2", "@smithy/shared-ini-file-loader": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-O1c5F1pbEImgEe3Vr8j1gpWu69UXWj3nN3vvLGh77hcrG5dZ8I27tSP5RN4Labm8Dnji/6ia+vqSYpN8w6KN5A=="],
|
||||
|
||||
"@aws-sdk/types": ["@aws-sdk/types@3.901.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-FfEM25hLEs4LoXsLXQ/q6X6L4JmKkKkbVFpKD4mwfVHtRVQG6QxJiCPcrkcPISquiy6esbwK2eh64TWbiD60cg=="],
|
||||
"@aws-sdk/types": ["@aws-sdk/types@3.910.0", "", { "dependencies": { "@smithy/types": "^4.7.1", "tslib": "^2.6.2" } }, "sha512-o67gL3vjf4nhfmuSUNNkit0d62QJEwwHLxucwVJkR/rw9mfUtAWsgBs8Tp16cdUbMgsyQtCQilL8RAJDoGtadQ=="],
|
||||
|
||||
"@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.901.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "@smithy/util-endpoints": "^3.2.0", "tslib": "^2.6.2" } }, "sha512-5nZP3hGA8FHEtKvEQf4Aww5QZOkjLW1Z+NixSd+0XKfHvA39Ah5sZboScjLx0C9kti/K3OGW1RCx5K9Zc3bZqg=="],
|
||||
"@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "@smithy/url-parser": "^4.2.2", "@smithy/util-endpoints": "^3.2.2", "tslib": "^2.6.2" } }, "sha512-6XgdNe42ibP8zCQgNGDWoOF53RfEKzpU/S7Z29FTTJ7hcZv0SytC0ZNQQZSx4rfBl036YWYwJRoJMlT4AA7q9A=="],
|
||||
|
||||
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.893.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg=="],
|
||||
|
||||
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.907.0", "", { "dependencies": { "@aws-sdk/types": "3.901.0", "@smithy/types": "^4.6.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-Hus/2YCQmtCEfr4Ls88d07Q99Ex59uvtktiPTV963Q7w7LHuIT/JBjrbwNxtSm2KlJR9PHNdqxwN+fSuNsMGMQ=="],
|
||||
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.910.0", "", { "dependencies": { "@aws-sdk/types": "3.910.0", "@smithy/types": "^4.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-iOdrRdLZHrlINk9pezNZ82P/VxO/UmtmpaOAObUN+xplCUJu31WNM2EE/HccC8PQw6XlAudpdA6HDTGiW6yVGg=="],
|
||||
|
||||
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.908.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.908.0", "@aws-sdk/types": "3.901.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-l6AEaKUAYarcEy8T8NZ+dNZ00VGLs3fW2Cqu1AuPENaSad0/ahEU+VU7MpXS8FhMRGPgplxKVgCTLyTY0Lbssw=="],
|
||||
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.911.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.911.0", "@aws-sdk/types": "3.910.0", "@smithy/node-config-provider": "^4.3.2", "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-3l+f6ooLF6Z6Lz0zGi7vSKSUYn/EePPizv88eZQpEAFunBHv+CSVNPtxhxHfkm7X9tTsV4QGZRIqo3taMLolmA=="],
|
||||
|
||||
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.901.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-pxFCkuAP7Q94wMTNPAwi6hEtNrp/BdFf+HOrIEeFQsk4EoOmpKY3I6S+u6A9Wg295J80Kh74LqDWM22ux3z6Aw=="],
|
||||
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.911.0", "", { "dependencies": { "@smithy/types": "^4.7.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-/yh3oe26bZfCVGrIMRM9Z4hvvGJD+qx5tOLlydOkuBkm72aXON7D9+MucjJXTAcI8tF2Yq+JHa0478eHQOhnLg=="],
|
||||
|
||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.0.1", "", {}, "sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw=="],
|
||||
|
||||
@ -662,57 +662,57 @@
|
||||
|
||||
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="],
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="],
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="],
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="],
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="],
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="],
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="],
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="],
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="],
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="],
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="],
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="],
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="],
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="],
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="],
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="],
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="],
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="],
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="],
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="],
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="],
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="],
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="],
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="],
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="],
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="],
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="],
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
|
||||
|
||||
@ -754,7 +754,7 @@
|
||||
|
||||
"@iconify-json/mdi": ["@iconify-json/mdi@1.2.3", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg=="],
|
||||
|
||||
"@iconify/json": ["@iconify/json@2.2.395", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.0" } }, "sha512-XSYOnlGqiZhJkFFBUiVK4C5VIiv4rxyKtCmkQ9nS4zfMpS4xT0BF9+qWUKOHYgeCzCLghyWfrm6Eti3Sv5kfqQ=="],
|
||||
"@iconify/json": ["@iconify/json@2.2.398", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.0" } }, "sha512-SEUDjkyAenAKMd5hDfzNzeObZw+KgQdIHaOuWA4yv89qSf2s0dJ7L1iXhRjtEZxFnuXQ2D67uFiHmE+90jvDGQ=="],
|
||||
|
||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||
|
||||
@ -766,11 +766,11 @@
|
||||
|
||||
"@intlify/bundle-utils": ["@intlify/bundle-utils@10.0.1", "", { "dependencies": { "@intlify/message-compiler": "^11.1.2", "@intlify/shared": "^11.1.2", "acorn": "^8.8.2", "escodegen": "^2.1.0", "estree-walker": "^2.0.2", "jsonc-eslint-parser": "^2.3.0", "mlly": "^1.2.0", "source-map-js": "^1.0.1", "yaml-eslint-parser": "^1.2.2" } }, "sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ=="],
|
||||
|
||||
"@intlify/core-base": ["@intlify/core-base@9.14.5", "", { "dependencies": { "@intlify/message-compiler": "9.14.5", "@intlify/shared": "9.14.5" } }, "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA=="],
|
||||
"@intlify/core-base": ["@intlify/core-base@9.13.1", "", { "dependencies": { "@intlify/message-compiler": "9.13.1", "@intlify/shared": "9.13.1" } }, "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w=="],
|
||||
|
||||
"@intlify/eslint-plugin-vue-i18n": ["@intlify/eslint-plugin-vue-i18n@1.4.1", "", { "dependencies": { "@eslint/eslintrc": "^1.2.0", "@intlify/core-base": "^9.1.9", "@intlify/message-compiler": "^9.1.9", "debug": "^4.3.1", "glob": "^7.1.3", "ignore": "^5.0.5", "is-language-code": "^3.1.0", "js-yaml": "^4.0.0", "json5": "^2.1.3", "jsonc-eslint-parser": "^2.0.0", "lodash": "^4.17.11", "parse5": "^6.0.0", "semver": "^7.3.4", "vue-eslint-parser": "^8.0.0", "yaml-eslint-parser": "^0.5.0" }, "peerDependencies": { "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-vnhwxcUTYCL/tCeBkXMDz959DVHNaDd3SRt3jdyX5ZwHaSSx93aD7kZV7ZmJpq4lZlq7Q1eVRGhpmpTNGdvU9w=="],
|
||||
|
||||
"@intlify/message-compiler": ["@intlify/message-compiler@9.14.5", "", { "dependencies": { "@intlify/shared": "9.14.5", "source-map-js": "^1.0.2" } }, "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ=="],
|
||||
"@intlify/message-compiler": ["@intlify/message-compiler@9.13.1", "", { "dependencies": { "@intlify/shared": "9.13.1", "source-map-js": "^1.0.2" } }, "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w=="],
|
||||
|
||||
"@intlify/shared": ["@intlify/shared@9.13.1", "", {}, "sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ=="],
|
||||
|
||||
@ -830,7 +830,7 @@
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@keyv/bigmap": ["@keyv/bigmap@1.0.3", "", { "dependencies": { "hookified": "^1.12.1" } }, "sha512-jUEkNlnE9tYzX2AIBeoSe1gVUvSOfIOQ5EFPL5Un8cFHGvjD9L/fxpxlS1tEivRLHgapO2RZJ3D93HYAa049pg=="],
|
||||
"@keyv/bigmap": ["@keyv/bigmap@1.1.0", "", { "dependencies": { "hookified": "^1.12.2" }, "peerDependencies": { "keyv": "^5.5.3" } }, "sha512-MX7XIUNwVRK+hjZcAbNJ0Z8DREo+Weu9vinBOjGU1thEi9F6vPhICzBbk4CCf3eEefKRz7n6TfZXwUFZTSgj8Q=="],
|
||||
|
||||
"@keyv/serialize": ["@keyv/serialize@1.1.1", "", {}, "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA=="],
|
||||
|
||||
@ -900,43 +900,43 @@
|
||||
|
||||
"@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="],
|
||||
|
||||
"@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.9.0", "", { "os": "android", "cpu": "arm" }, "sha512-4AxaG6TkSBQ2FiC5oGZEJQ35DjsSfAbW6/AJauebq4EzIPVOIgDJCF4de+PvX/Xi9BkNw6VtJuMXJdWW97iEAA=="],
|
||||
"@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.11.0", "", { "os": "android", "cpu": "arm" }, "sha512-aN0UJg1xr0N1dADQ135z4p3bP9AYAUN1Ey2VvLMK6IwWYIJGWpKT+cr1l3AiyBeLK8QZyFDb4IDU8LHgjO9TDQ=="],
|
||||
|
||||
"@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.9.0", "", { "os": "android", "cpu": "arm64" }, "sha512-oOEg7rUd2M6YlmRkvPcszJ6KO6TaLGN21oDdcs27gbTVYbQQtCWYbZz5jRW5zEBJu6dopoWVx+shJNGtG1qDFw=="],
|
||||
"@oxc-resolver/binding-android-arm64": ["@oxc-resolver/binding-android-arm64@11.11.0", "", { "os": "android", "cpu": "arm64" }, "sha512-FckvvMclo8CSJqQjKpHueIIbKrg9L638NKWQTiJQaD8W9F61h8hTjF8+QFLlCHh6R9RcE5roVHdkkiBKHlB2Zw=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.9.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fM6zE/j6o3C1UIkcZPV7C1f186R7w97guY2N4lyNLlhlgwwhd46acnOezLARvRNU5oyKNev4PvOJhGCCDnFMGg=="],
|
||||
"@oxc-resolver/binding-darwin-arm64": ["@oxc-resolver/binding-darwin-arm64@11.11.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7ZcpgaXSBnwRHM1YR8Vazq7mCTtGdYRvM7k46CscA+oipCVqmI4LbW2wLsc6HVjqX+SM/KPOfFGoGjEgmQPFTQ=="],
|
||||
|
||||
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.9.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Bg3Orw7gAxbUqQlt64YPWvHDVo3bo2JfI26Qmzv6nKo7mIMTDhQKl7YmywtLNMYbX0IgUM4qu1V90euu+WCDOw=="],
|
||||
"@oxc-resolver/binding-darwin-x64": ["@oxc-resolver/binding-darwin-x64@11.11.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-Wsd1JWORokMmOKrR4t4jxpwYEWG11+AHWu9bdzjCO5EIyi0AuNpPIAEcEFCP9FNd0h8c+VUYbMRU/GooD2zOIg=="],
|
||||
|
||||
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.9.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-eBqVZqTETH6miBfIZXvpzUe98WATz2+Sh+LEFwuRpGsTsKkIpTyb4p1kwylCLkxrd3Yx7wkxQku+L0AMEGBiAA=="],
|
||||
"@oxc-resolver/binding-freebsd-x64": ["@oxc-resolver/binding-freebsd-x64@11.11.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YX+W10kHrMouu/+Y+rqJdCWO3dFBKM1DIils30PHsmXWp1v+ZZvhibaST2BP6zrWkWquZ8pMmsObD6N10lLgiA=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.9.0", "", { "os": "linux", "cpu": "arm" }, "sha512-QgCk/IJnGBvpbc8rYTVgO+A3m3edJjH1zfv8Nvx7fmsxpbXwWH2l4b4tY3/SLMzasxsp7x7k87+HWt095bI5Lg=="],
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf": ["@oxc-resolver/binding-linux-arm-gnueabihf@11.11.0", "", { "os": "linux", "cpu": "arm" }, "sha512-UAhlhVkW2ui98bClmEkDLKQz4XBSccxMahG7rMeX2RepS2QByAWxYFFThaNbHtBSB+B4Rc1hudkihq8grQkU3g=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.9.0", "", { "os": "linux", "cpu": "arm" }, "sha512-xkJH0jldIXD2GwoHpCDEF0ucJ7fvRETCL+iFLctM679o7qeDXvtzsO/E401EgFFXcWBJNKXWvH+ZfdYMKyowfA=="],
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf": ["@oxc-resolver/binding-linux-arm-musleabihf@11.11.0", "", { "os": "linux", "cpu": "arm" }, "sha512-5pEliabSEiimXz/YyPxzyBST82q8PbM6BoEMS8kOyaDbEBuzTr7pWU1U0F7ILGBFjJmHaj3N7IAhQgeXdpdySg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-TWq+y2psMzbMtZB9USAq2bSA7NV1TMmh9lhAFbMGQ8Yp2YV4BRC/HilD6qF++efQl6shueGBFOv0LVe9BUXaIA=="],
|
||||
"@oxc-resolver/binding-linux-arm64-gnu": ["@oxc-resolver/binding-linux-arm64-gnu@11.11.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-CiyufPFIOJrW/HovAMGsH0AbV7BSCb0oE0KDtt7z1+e+qsDo7HRlTSnqE3JbNuhJRg3Cz/j7qEYzgGqco9SE4Q=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.9.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8WwGLfXk7yttc6rD6g53+RnYfX5B8xOot1ffthLn8oCXzVRO4cdChlmeHStxwLD/MWx8z8BGeyfyINNrsh9N2w=="],
|
||||
"@oxc-resolver/binding-linux-arm64-musl": ["@oxc-resolver/binding-linux-arm64-musl@11.11.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-w07MfGtDLZV0rISdXl2cGASxD/sRrrR93Qd4q27O2Hsky4MGbLw94trbzhmAkc7OKoJI0iDg1217i3jfxmVk1Q=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.9.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZWiAXfan6actlSzayaFS/kYO2zD6k1k0fmLb1opbujXYMKepEnjjVOvKdzCIYR/zKzudqI39dGc+ywqVdsPIpQ=="],
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu": ["@oxc-resolver/binding-linux-ppc64-gnu@11.11.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gzM+ZfIjfcCofwX/m1eLCoTT+3T70QLWaKDOW5Hf3+ddLlxMEVRIQtUoRsp0e/VFanr7u7VKS57TxhkRubseNg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.9.0", "", { "os": "linux", "cpu": "none" }, "sha512-p9mCSb+Bym+eycNo9k+81wQ5SAE31E+/rtfbDmF4/7krPotkEjPsEBSc3rqunRwO+FtsUn7H68JLY7hlai49eQ=="],
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu": ["@oxc-resolver/binding-linux-riscv64-gnu@11.11.0", "", { "os": "linux", "cpu": "none" }, "sha512-oCR0ImJQhIwmqwNShsRT0tGIgKF5/H4nhtIEkQAQ9bLzMgjtRqIrZ3DtGHqd7w58zhXWfIZdyPNF9IrSm+J/fQ=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.9.0", "", { "os": "linux", "cpu": "none" }, "sha512-/SePuVxgFhLPciRwsJ8kLVltr+rxh0b6riGFuoPnFXBbHFclKnjNIt3TfqzUj0/vOnslXw3cVGPpmtkm2TgCgg=="],
|
||||
"@oxc-resolver/binding-linux-riscv64-musl": ["@oxc-resolver/binding-linux-riscv64-musl@11.11.0", "", { "os": "linux", "cpu": "none" }, "sha512-MjCEqsUzXMfWPfsEUX+UXttzXz6xiNU11r7sj00C5og/UCyqYw1OjrbC/B1f/dloDpTn0rd4xy6c/LTvVQl2tg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.9.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-zLuEjlYIzfnr1Ei2UZYQBbCTa/9deh+BEjO9rh1ai8BfEq4uj6RupTtNpgHfgAsEYdqOBVExw9EU1S6SW3RCAw=="],
|
||||
"@oxc-resolver/binding-linux-s390x-gnu": ["@oxc-resolver/binding-linux-s390x-gnu@11.11.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-4TaTX7gT3357vWQsTe3IfDtWyJNe0FejypQ4ngwxB3v1IVaW6KAUt0huSvx/tmj+YWxd3zzXdWd8AzW0jo6dpg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cxdg73WG+aVlPu/k4lEQPRVOhWunYOUglW6OSzclZLJJAXZU0tSZ5ymKaqPRkfTsyNSAafj1cA1XYd+P9UxBgw=="],
|
||||
"@oxc-resolver/binding-linux-x64-gnu": ["@oxc-resolver/binding-linux-x64-gnu@11.11.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ch1o3+tBra9vmrgXqrufVmYnvRPFlyUb7JWs/VXndBmyNSuP2KP+guAUrC0fr2aSGoOQOasAiZza7MTFU7Vrxg=="],
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.9.0", "", { "os": "linux", "cpu": "x64" }, "sha512-sy5nkVdMvNgqcx9sIY7G6U9TYZUZC4cmMGw/wKhJNuuD2/HFGtbje62ttXSwBAbVbmJ2GgZ4ZUo/S1OMyU+/OA=="],
|
||||
"@oxc-resolver/binding-linux-x64-musl": ["@oxc-resolver/binding-linux-x64-musl@11.11.0", "", { "os": "linux", "cpu": "x64" }, "sha512-llTdl2gJAqXaGV7iV1w5BVlqXACcoT1YD3o840pCQx1ZmKKAAz7ydPnTjYVdkGImXNWPOIWJixHW0ryDm4Mx7w=="],
|
||||
|
||||
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.9.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.5" }, "cpu": "none" }, "sha512-dfi/a0Xh6o6nOLbJdaYuy7txncEcwkRHp9DGGZaAP7zxDiepkBZ6ewSJODQrWwhjVmMteXo+XFzEOMjsC7WUtQ=="],
|
||||
"@oxc-resolver/binding-wasm32-wasi": ["@oxc-resolver/binding-wasm32-wasi@11.11.0", "", { "dependencies": { "@napi-rs/wasm-runtime": "^1.0.7" }, "cpu": "none" }, "sha512-cROavohP0nX91NtIVVgOTugqoxlUSNxI9j7MD+B7fmD3gEFl8CVyTamR0/p6loDxLv51bQYTHRKn/ZYTd3ENzw=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.9.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-b1yKr+eFwyi8pZMjAQwW352rXpaHAmz7FLK03vFIxdyWzWiiL6S3UrfMu+nKQud38963zu4wNNLm7rdXQazgRA=="],
|
||||
"@oxc-resolver/binding-win32-arm64-msvc": ["@oxc-resolver/binding-win32-arm64-msvc@11.11.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-6amVs34yHmxE6Q3CtTPXnSvIYGqwQJ/lVVRYccLzg9smge3WJ1knyBV5jpKKayp0n316uPYzB4EgEbgcuRvrPw=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.9.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-DxRT+1HjCpRH8qYCmGHzgsRCYiK+X14PUM9Fb+aD4TljplA7MdDQXqMISTb4zBZ70AuclvlXKTbW+K1GZop3xA=="],
|
||||
"@oxc-resolver/binding-win32-ia32-msvc": ["@oxc-resolver/binding-win32-ia32-msvc@11.11.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-v/IZ5s2/3auHUoi0t6Ea1CDsWxrE9BvgvbDcJ04QX+nEbmTBazWPZeLsH8vWkRAh8EUKCZHXxjQsPhEH5Yk5pQ=="],
|
||||
|
||||
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.9.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gE3QJvhh0Yj9cSAkkHjRLKPmC7BTJeiaB5YyhVKVUwbnWQgTszV92lZ9pvZtNPEghP7jPbhEs4c6983A0ojQwA=="],
|
||||
"@oxc-resolver/binding-win32-x64-msvc": ["@oxc-resolver/binding-win32-x64-msvc@11.11.0", "", { "os": "win32", "cpu": "x64" }, "sha512-qvm+IQ6r2q4HZitSV69O+OmvCD1y4pH7SbhR6lPwLsfZS5QRHS8V20VHxmG1jJzSPPw7S8Bb1rdNcxDSqc4bYA=="],
|
||||
|
||||
"@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="],
|
||||
|
||||
@ -994,49 +994,49 @@
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.4", "", { "os": "android", "cpu": "arm" }, "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA=="],
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.4", "", { "os": "android", "cpu": "arm64" }, "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w=="],
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg=="],
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw=="],
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ=="],
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw=="],
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ=="],
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q=="],
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg=="],
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g=="],
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ=="],
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g=="],
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg=="],
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA=="],
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA=="],
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg=="],
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw=="],
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.4", "", { "os": "none", "cpu": "arm64" }, "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA=="],
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ=="],
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw=="],
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ=="],
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w=="],
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
|
||||
|
||||
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
||||
|
||||
@ -1056,55 +1056,55 @@
|
||||
|
||||
"@sinonjs/fake-timers": ["@sinonjs/fake-timers@8.1.0", "", { "dependencies": { "@sinonjs/commons": "^1.7.0" } }, "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg=="],
|
||||
|
||||
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg=="],
|
||||
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-xWL9Mf8b7tIFuAlpjKtRPnHrR8XVrwTj5NPYO/QwZPtc0SDLsPxb56V5tzi5yspSMytISHybifez+4jlrx0vkQ=="],
|
||||
|
||||
"@smithy/config-resolver": ["@smithy/config-resolver@4.3.0", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ=="],
|
||||
"@smithy/config-resolver": ["@smithy/config-resolver@4.3.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/types": "^4.8.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-xSql8A1Bl41O9JvGU/CtgiLBlwkvpHTSKRlvz9zOBvBCPjXghZ6ZkcVzmV2f7FLAA+80+aqKmIOmy8pEDrtCaw=="],
|
||||
|
||||
"@smithy/core": ["@smithy/core@3.15.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-stream": "^4.5.0", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA=="],
|
||||
"@smithy/core": ["@smithy/core@3.17.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-stream": "^4.5.3", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Tir3DbfoTO97fEGUZjzGeoXgcQAUBRDTmuH9A8lxuP8ATrgezrAJ6cLuRvwdKN4ZbYNlHgKlBX69Hyu3THYhtg=="],
|
||||
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.0", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big=="],
|
||||
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/property-provider": "^4.2.3", "@smithy/types": "^4.8.0", "@smithy/url-parser": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-hA1MQ/WAHly4SYltJKitEsIDVsNmXcQfYBRv2e+q04fnqtAX5qXaybxy/fhUeAMCnQIdAjaGDb04fMHQefWRhw=="],
|
||||
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.1", "", { "dependencies": { "@smithy/protocol-http": "^5.3.0", "@smithy/querystring-builder": "^4.2.0", "@smithy/types": "^4.6.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q=="],
|
||||
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/querystring-builder": "^4.2.3", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-bwigPylvivpRLCm+YK9I5wRIYjFESSVwl8JQ1vVx/XhCw0PtCi558NwTnT2DaVCl5pYlImGuQTSwMsZ+pIavRw=="],
|
||||
|
||||
"@smithy/hash-node": ["@smithy/hash-node@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA=="],
|
||||
"@smithy/hash-node": ["@smithy/hash-node@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6+NOdZDbfuU6s1ISp3UOk5Rg953RJ2aBLNLLBEcamLjHAg1Po9Ha7QIB5ZWhdRUVuOUrT8BVFR+O2KIPmw027g=="],
|
||||
|
||||
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A=="],
|
||||
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-Cc9W5DwDuebXEDMpOpl4iERo8I0KFjTnomK2RMdhhR87GwrSmUmwMxS4P5JdRf+LsjOdIqumcerwRgYMr/tZ9Q=="],
|
||||
|
||||
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
|
||||
|
||||
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.0", "", { "dependencies": { "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ=="],
|
||||
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.3", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-/atXLsT88GwKtfp5Jr0Ks1CSa4+lB+IgRnkNrrYP0h1wL4swHNb0YONEvTceNKNdZGJsye+W2HH8W7olbcPUeA=="],
|
||||
|
||||
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.1", "", { "dependencies": { "@smithy/core": "^3.15.0", "@smithy/middleware-serde": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "@smithy/url-parser": "^4.2.0", "@smithy/util-middleware": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw=="],
|
||||
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.4", "", { "dependencies": { "@smithy/core": "^3.17.0", "@smithy/middleware-serde": "^4.2.3", "@smithy/node-config-provider": "^4.3.3", "@smithy/shared-ini-file-loader": "^4.3.3", "@smithy/types": "^4.8.0", "@smithy/url-parser": "^4.2.3", "@smithy/util-middleware": "^4.2.3", "tslib": "^2.6.2" } }, "sha512-/RJhpYkMOaUZoJEkddamGPPIYeKICKXOu/ojhn85dKDM0n5iDIhjvYAQLP3K5FPhgB203O3GpWzoK2OehEoIUw=="],
|
||||
|
||||
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.0", "@smithy/protocol-http": "^5.3.0", "@smithy/service-error-classification": "^4.2.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-retry": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w=="],
|
||||
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/protocol-http": "^5.3.3", "@smithy/service-error-classification": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-retry": "^4.2.3", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-vSgABQAkuUHRO03AhR2rWxVQ1un284lkBn+NFawzdahmzksAoOeVMnXXsuPViL4GlhRHXqFaMlc8Mj04OfQk1w=="],
|
||||
|
||||
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.0", "", { "dependencies": { "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw=="],
|
||||
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.3", "", { "dependencies": { "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-8g4NuUINpYccxiCXM5s1/V+uLtts8NcX4+sPEbvYQDZk4XoJfDpq5y2FQxfmUL89syoldpzNzA0R9nhzdtdKnQ=="],
|
||||
|
||||
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg=="],
|
||||
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-iGuOJkH71faPNgOj/gWuEGS6xvQashpLwWB1HjHq1lNNiVfbiJLpZVbhddPuDbx9l4Cgl0vPLq5ltRfSaHfspA=="],
|
||||
|
||||
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.0", "", { "dependencies": { "@smithy/property-provider": "^4.2.0", "@smithy/shared-ini-file-loader": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA=="],
|
||||
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.3", "", { "dependencies": { "@smithy/property-provider": "^4.2.3", "@smithy/shared-ini-file-loader": "^4.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-NzI1eBpBSViOav8NVy1fqOlSfkLgkUjUTlohUSgAEhHaFWA3XJiLditvavIP7OpvTjDp5u2LhtlBhkBlEisMwA=="],
|
||||
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.3.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/querystring-builder": "^4.2.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q=="],
|
||||
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.2", "", { "dependencies": { "@smithy/abort-controller": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/querystring-builder": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-MHFvTjts24cjGo1byXqhXrbqm7uznFD/ESFx8npHMWTFQVdBZjrT1hKottmp69LBTRm/JQzP/sn1vPt0/r6AYQ=="],
|
||||
|
||||
"@smithy/property-provider": ["@smithy/property-provider@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw=="],
|
||||
"@smithy/property-provider": ["@smithy/property-provider@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-+1EZ+Y+njiefCohjlhyOcy1UNYjT+1PwGFHCxA/gYctjg3DQWAU19WigOXAco/Ql8hZokNehpzLd0/+3uCreqQ=="],
|
||||
|
||||
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q=="],
|
||||
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-Mn7f/1aN2/jecywDcRDvWWWJF4uwg/A0XjFMJtj72DsgHTByfjRltSqcT9NyE9RTdBSN6X1RSXrhn/YWQl8xlw=="],
|
||||
|
||||
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A=="],
|
||||
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-LOVCGCmwMahYUM/P0YnU/AlDQFjcu+gWbFJooC417QRB/lDJlWSn8qmPSDp+s4YVAHOgtgbNG4sR+SxF/VOcJQ=="],
|
||||
|
||||
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA=="],
|
||||
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-cYlSNHcTAX/wc1rpblli3aUlLMGgKZ/Oqn8hhjFASXMCXjIqeuQBei0cnq2JR8t4RtU9FpG6uyl6PxyArTiwKA=="],
|
||||
|
||||
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0" } }, "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA=="],
|
||||
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0" } }, "sha512-NkxsAxFWwsPsQiwFG2MzJ/T7uIR6AQNh1SzcxSUnmmIqIQMlLRQDKhc17M7IYjiuBXhrQRjQTo3CxX+DobS93g=="],
|
||||
|
||||
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ=="],
|
||||
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-9f9Ixej0hFhroOK2TxZfUUDR13WVa8tQzhSzPDgXe5jGL3KmaM9s8XN7RQwqtEypI82q9KHnKS71CJ+q/1xLtQ=="],
|
||||
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.0", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g=="],
|
||||
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.3", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.3", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-CmSlUy+eEYbIEYN5N3vvQTRfqt0lJlQkaQUIf+oizu7BbDut0pozfDjBGecfcfWf7c62Yis4JIEgqQ/TCfodaA=="],
|
||||
|
||||
"@smithy/smithy-client": ["@smithy/smithy-client@4.7.1", "", { "dependencies": { "@smithy/core": "^3.15.0", "@smithy/middleware-endpoint": "^4.3.1", "@smithy/middleware-stack": "^4.2.0", "@smithy/protocol-http": "^5.3.0", "@smithy/types": "^4.6.0", "@smithy/util-stream": "^4.5.0", "tslib": "^2.6.2" } }, "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ=="],
|
||||
"@smithy/smithy-client": ["@smithy/smithy-client@4.9.0", "", { "dependencies": { "@smithy/core": "^3.17.0", "@smithy/middleware-endpoint": "^4.3.4", "@smithy/middleware-stack": "^4.2.3", "@smithy/protocol-http": "^5.3.3", "@smithy/types": "^4.8.0", "@smithy/util-stream": "^4.5.3", "tslib": "^2.6.2" } }, "sha512-qz7RTd15GGdwJ3ZCeBKLDQuUQ88m+skh2hJwcpPm1VqLeKzgZvXf6SrNbxvx7uOqvvkjCMXqx3YB5PDJyk00ww=="],
|
||||
|
||||
"@smithy/types": ["@smithy/types@4.6.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA=="],
|
||||
"@smithy/types": ["@smithy/types@4.8.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-QpELEHLO8SsQVtqP+MkEgCYTFW0pleGozfs3cZ183ZBj9z3VC1CX1/wtFMK64p+5bhtZo41SeLK1rBRtd25nHQ=="],
|
||||
|
||||
"@smithy/url-parser": ["@smithy/url-parser@4.2.0", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A=="],
|
||||
"@smithy/url-parser": ["@smithy/url-parser@4.2.3", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-I066AigYvY3d9VlU3zG9XzZg1yT10aNqvCaBTw9EPgu5GrsEl1aUkcMvhkIXascYH1A8W0LQo3B1Kr1cJNcQEw=="],
|
||||
|
||||
"@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
|
||||
|
||||
@ -1116,25 +1116,25 @@
|
||||
|
||||
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
|
||||
|
||||
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.0", "", { "dependencies": { "@smithy/property-provider": "^4.2.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ=="],
|
||||
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.3", "", { "dependencies": { "@smithy/property-provider": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-vqHoybAuZXbFXZqgzquiUXtdY+UT/aU33sxa4GBPkiYklmR20LlCn+d3Wc3yA5ZM13gQ92SZe/D8xh6hkjx+IQ=="],
|
||||
|
||||
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.1", "", { "dependencies": { "@smithy/config-resolver": "^4.3.0", "@smithy/credential-provider-imds": "^4.2.0", "@smithy/node-config-provider": "^4.3.0", "@smithy/property-provider": "^4.2.0", "@smithy/smithy-client": "^4.7.1", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug=="],
|
||||
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.4", "", { "dependencies": { "@smithy/config-resolver": "^4.3.3", "@smithy/credential-provider-imds": "^4.2.3", "@smithy/node-config-provider": "^4.3.3", "@smithy/property-provider": "^4.2.3", "@smithy/smithy-client": "^4.9.0", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-X5/xrPHedifo7hJUUWKlpxVb2oDOiqPUXlvsZv1EZSjILoutLiJyWva3coBpn00e/gPSpH8Rn2eIbgdwHQdW7Q=="],
|
||||
|
||||
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.0", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg=="],
|
||||
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.3", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-aCfxUOVv0CzBIkU10TubdgKSx5uRvzH064kaiPEWfNIvKOtNpu642P4FP1hgOFkjQIkDObrfIDnKMKkeyrejvQ=="],
|
||||
|
||||
"@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
|
||||
|
||||
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.0", "", { "dependencies": { "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA=="],
|
||||
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.3", "", { "dependencies": { "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-v5ObKlSe8PWUHCqEiX2fy1gNv6goiw6E5I/PN2aXg3Fb/hse0xeaAnSpXDiWl7x6LamVKq7senB+m5LOYHUAHw=="],
|
||||
|
||||
"@smithy/util-retry": ["@smithy/util-retry@4.2.0", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg=="],
|
||||
"@smithy/util-retry": ["@smithy/util-retry@4.2.3", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-lLPWnakjC0q9z+OtiXk+9RPQiYPNAovt2IXD3CP4LkOnd9NpUsxOjMx1SnoUVB7Orb7fZp67cQMtTBKMFDvOGg=="],
|
||||
|
||||
"@smithy/util-stream": ["@smithy/util-stream@4.5.0", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.1", "@smithy/node-http-handler": "^4.3.0", "@smithy/types": "^4.6.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw=="],
|
||||
"@smithy/util-stream": ["@smithy/util-stream@4.5.3", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.4", "@smithy/node-http-handler": "^4.4.2", "@smithy/types": "^4.8.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-oZvn8a5bwwQBNYHT2eNo0EU8Kkby3jeIg1P2Lu9EQtqDxki1LIjGRJM6dJ5CZUig8QmLxWxqOKWvg3mVoOBs5A=="],
|
||||
|
||||
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
|
||||
|
||||
"@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
|
||||
|
||||
"@smithy/util-waiter": ["@smithy/util-waiter@4.2.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.0", "@smithy/types": "^4.6.0", "tslib": "^2.6.2" } }, "sha512-0Z+nxUU4/4T+SL8BCNN4ztKdQjToNvUYmkF1kXO5T7Yz3Gafzh0HeIG6mrkN8Fz3gn9hSyxuAT+6h4vM+iQSBQ=="],
|
||||
"@smithy/util-waiter": ["@smithy/util-waiter@4.2.3", "", { "dependencies": { "@smithy/abort-controller": "^4.2.3", "@smithy/types": "^4.8.0", "tslib": "^2.6.2" } }, "sha512-5+nU///E5sAdD7t3hs4uwvCTWQtTR8JwKwOCSJtBRx0bY1isDo1QwH87vRK86vlFLBTISqoDA2V6xvP6nF1isQ=="],
|
||||
|
||||
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
||||
|
||||
@ -1296,7 +1296,7 @@
|
||||
|
||||
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
|
||||
|
||||
"@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="],
|
||||
"@types/send": ["@types/send@1.2.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ=="],
|
||||
|
||||
"@types/serve-static": ["@types/serve-static@1.15.9", "", { "dependencies": { "@types/http-errors": "*", "@types/node": "*", "@types/send": "<1" } }, "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA=="],
|
||||
|
||||
@ -1316,19 +1316,19 @@
|
||||
|
||||
"@types/zen-observable": ["@types/zen-observable@0.8.7", "", {}, "sha512-LKzNTjj+2j09wAo/vvVjzgw5qckJJzhdGgWHW7j69QIGdq/KnZrMAMIHQiWGl3Ccflh5/CudBAntTPYdprPltA=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.0", "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ=="],
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.1", "@typescript-eslint/types": "^8.46.1", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0" } }, "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw=="],
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.1", "", { "dependencies": { "@typescript-eslint/types": "8.46.1", "@typescript-eslint/visitor-keys": "8.46.1" } }, "sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw=="],
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.46.0", "", {}, "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA=="],
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.46.1", "", {}, "sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.0", "@typescript-eslint/tsconfig-utils": "8.46.0", "@typescript-eslint/types": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg=="],
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.1", "@typescript-eslint/tsconfig-utils": "8.46.1", "@typescript-eslint/types": "8.46.1", "@typescript-eslint/visitor-keys": "8.46.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@7.18.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", "@typescript-eslint/typescript-estree": "7.18.0" }, "peerDependencies": { "eslint": "^8.56.0" } }, "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.0", "", { "dependencies": { "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q=="],
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.1", "", { "dependencies": { "@typescript-eslint/types": "8.46.1", "eslint-visitor-keys": "^4.2.1" } }, "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||
|
||||
@ -1618,11 +1618,11 @@
|
||||
|
||||
"balanced-match": ["balanced-match@2.0.0", "", {}, "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA=="],
|
||||
|
||||
"bare-addon-resolve": ["bare-addon-resolve@1.9.4", "", { "dependencies": { "bare-module-resolve": "^1.10.0", "bare-semver": "^1.0.0" }, "peerDependencies": { "bare-url": "*" }, "optionalPeers": ["bare-url"] }, "sha512-unn6Vy/Yke6F99vg/7tcrvM2KUvIhTNniaSqDbam4AWkd4NhvDVSrQiRYVlNzUV2P7SPobkCK7JFVxrJk9btCg=="],
|
||||
"bare-addon-resolve": ["bare-addon-resolve@1.9.5", "", { "dependencies": { "bare-module-resolve": "^1.10.0", "bare-semver": "^1.0.0" }, "peerDependencies": { "bare-url": "*" }, "optionalPeers": ["bare-url"] }, "sha512-XdqrG73zLK9LDfblOJwoAxmJ+7YdfRW4ex46+f4L+wPhk7H7LDrRMAbBw8s8jkxeEFpUenyB7QHnv0ErAWd3Yg=="],
|
||||
|
||||
"bare-events": ["bare-events@2.8.0", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA=="],
|
||||
|
||||
"bare-module-resolve": ["bare-module-resolve@1.11.1", "", { "dependencies": { "bare-semver": "^1.0.0" }, "peerDependencies": { "bare-url": "*" }, "optionalPeers": ["bare-url"] }, "sha512-DCxeT9i8sTs3vUMA3w321OX/oXtNEu5EjObQOnTmCdNp5RXHBAvAaBDHvAi9ta0q/948QPz+co6SsGi6aQMYRg=="],
|
||||
"bare-module-resolve": ["bare-module-resolve@1.11.2", "", { "dependencies": { "bare-semver": "^1.0.0" }, "peerDependencies": { "bare-url": "*" }, "optionalPeers": ["bare-url"] }, "sha512-HIBu9WacMejg3Dz4X1v6lJjp7ECnwpujvuLub+8I7JJLRwJaGxWMzGYvieOoS9R1n5iRByvTmLtIdPbwjfRgiQ=="],
|
||||
|
||||
"bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="],
|
||||
|
||||
@ -1630,11 +1630,11 @@
|
||||
|
||||
"bare-semver": ["bare-semver@1.0.1", "", {}, "sha512-UtggzHLiTrmFOC/ogQ+Hy7VfoKoIwrP1UFcYtTxoCUdLtsIErT8+SWtOC2DH/snT9h+xDrcBEPcwKei1mzemgg=="],
|
||||
|
||||
"bare-url": ["bare-url@2.3.0", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-c+RCqMSZbkz97Mw1LWR0gcOqwK82oyYKfLoHJ8k13ybi1+I80ffdDzUy0TdAburdrR/kI0/VuN8YgEnJqX+Nyw=="],
|
||||
"bare-url": ["bare-url@2.3.1", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-v2yl0TnaZTdEnelkKtXZGnotiV6qATBlnNuUMrHl6v9Lmmrh9mw9RYyImPU7/4RahumSwQS1k2oKXcRfXcbjJw=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-OMu3BGQ4E7P1ErFsIPpbJh0qvDudM/UuJeHgkAvfWe+0HFJCXh+t/l8L6fVLR55RI/UbKrVLnAXZSVwd9ysWYw=="],
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.8.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w=="],
|
||||
|
||||
"bbump": ["bbump@1.0.2", "", { "dependencies": { "inquirer": "^9.0.0" }, "bin": { "bbump": "index.js" } }, "sha512-JGwlqjBF9cvPCjONPlb8R7YH4Uum8E06MJchUxpbjr2Ft7v/hjq+mM7zq93anI0Ww7sg9WAQulzCVDGUnd9YdQ=="],
|
||||
|
||||
@ -1692,11 +1692,11 @@
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
"c12": ["c12@3.3.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.2", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw=="],
|
||||
"c12": ["c12@3.3.1", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.3", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.0.0", "pkg-types": "^2.3.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-LcWQ01LT9tkoUINHgpIOv3mMs+Abv7oVCrtpMRi1PaapVEpWoMga5WuT7/DqFTu7URP9ftbOmimNw1KNIGh9DQ=="],
|
||||
|
||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"cacheable": ["cacheable@2.1.0", "", { "dependencies": { "@cacheable/memoize": "^2.0.3", "@cacheable/memory": "^2.0.3", "@cacheable/utils": "^2.1.0", "hookified": "^1.12.1", "keyv": "^5.5.3", "qified": "^0.5.0" } }, "sha512-zzL1BxdnqwD69JRT0dihnawAcLkBMwAH+hZSKjUzeBbPedVhk3qYPjRw9VOMYWwt5xRih5xd8S+3kEdGohZm/g=="],
|
||||
"cacheable": ["cacheable@2.1.1", "", { "dependencies": { "@cacheable/memoize": "^2.0.3", "@cacheable/memory": "^2.0.3", "@cacheable/utils": "^2.1.0", "hookified": "^1.12.2", "keyv": "^5.5.3", "qified": "^0.5.0" } }, "sha512-LmF4AXiSNdiRbI2UjH8pAp9NIXxeQsTotpEaegPiDcnN0YPygDJDV3l/Urc0mL72JWdATEorKqIHEx55nDlONg=="],
|
||||
|
||||
"cacheable-lookup": ["cacheable-lookup@7.0.0", "", {}, "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="],
|
||||
|
||||
@ -1714,7 +1714,7 @@
|
||||
|
||||
"camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001750", "", {}, "sha512-cuom0g5sdX6rw00qOoLNSFCJ9/mYIsuSOA+yzpDw8eopiFqcVwQvZHqov0vmEighRxX++cfC0Vg1G+1Iy/mSpQ=="],
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001751", "", {}, "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw=="],
|
||||
|
||||
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||
|
||||
@ -1760,7 +1760,7 @@
|
||||
|
||||
"co": ["co@4.6.0", "", {}, "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ=="],
|
||||
|
||||
"collect-v8-coverage": ["collect-v8-coverage@1.0.2", "", {}, "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q=="],
|
||||
"collect-v8-coverage": ["collect-v8-coverage@1.0.3", "", {}, "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
@ -1774,7 +1774,7 @@
|
||||
|
||||
"commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
|
||||
|
||||
"compact-encoding": ["compact-encoding@2.17.0", "", { "dependencies": { "b4a": "^1.3.0" } }, "sha512-A5mkpopiDqCddAHTmJ/VnP7JQ1MCytRbpi13zQJGsDcKN041B++2n8oUwxBn37C86gbIt/zIARaqLVy9vI9M5Q=="],
|
||||
"compact-encoding": ["compact-encoding@2.18.0", "", { "dependencies": { "b4a": "^1.3.0" } }, "sha512-goACAOlhMI2xo5jGOMUDfOLnGdRE1jGfyZ+zie8N5114nHrbPIqf6GLUtzbLof6DSyrERlYRm3EcBplte5LcQw=="],
|
||||
|
||||
"compact-encoding-net": ["compact-encoding-net@1.2.0", "", { "dependencies": { "compact-encoding": "^2.4.1" } }, "sha512-LVXpNpF7PGQeHRVVLGgYWzuVoYAaDZvKUsUxRioGfkotzvOh4AzoQF1HBH3zMNaSnx7gJXuUr3hkjnijaH/Eng=="],
|
||||
|
||||
@ -1800,7 +1800,7 @@
|
||||
|
||||
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
"convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
||||
|
||||
"cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
|
||||
|
||||
@ -1962,7 +1962,7 @@
|
||||
|
||||
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.234", "", {}, "sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg=="],
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.237", "", {}, "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg=="],
|
||||
|
||||
"email-templates": ["email-templates@10.0.1", "", { "dependencies": { "@ladjs/i18n": "^8.0.1", "consolidate": "^0.16.0", "get-paths": "^0.0.7", "html-to-text": "^8.2.0", "juice": "^8.0.0", "lodash": "^4.17.21", "nodemailer": "^6.7.7", "preview-email": "^3.0.7" } }, "sha512-LNZKS0WW9XQkjuDZd/4p/1Q/pwqaqXOP3iDxTIVIQY9vuHlIUEcRLFo8/Xh3GtZCBnm181VgvOXIABKTVyTePA=="],
|
||||
|
||||
@ -2004,7 +2004,7 @@
|
||||
|
||||
"es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="],
|
||||
"esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
@ -2266,7 +2266,7 @@
|
||||
|
||||
"graphql-request": ["graphql-request@5.0.0", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "cross-fetch": "^3.1.5", "extract-files": "^9.0.0", "form-data": "^3.0.0" }, "peerDependencies": { "graphql": "14 - 16" } }, "sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw=="],
|
||||
|
||||
"graphql-scalars": ["graphql-scalars@1.24.2", "", { "dependencies": { "tslib": "^2.5.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" } }, "sha512-FoZ11yxIauEnH0E5rCUkhDXHVn/A6BBfovJdimRZCQlFCl+h7aVvarKmI15zG4VtQunmCDdqdtNs6ixThy3uAg=="],
|
||||
"graphql-scalars": ["graphql-scalars@1.25.0", "", { "dependencies": { "tslib": "^2.5.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" } }, "sha512-b0xyXZeRFkne4Eq7NAnL400gStGqG/Sx9VqX0A05nHyEbv57UJnWKsjNnrpVqv5e/8N1MUxkt0wwcRXbiyKcFg=="],
|
||||
|
||||
"graphql-subscriptions": ["graphql-subscriptions@1.2.1", "", { "dependencies": { "iterall": "^1.3.0" }, "peerDependencies": { "graphql": "^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0" } }, "sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g=="],
|
||||
|
||||
@ -2302,7 +2302,7 @@
|
||||
|
||||
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
|
||||
|
||||
"hookified": ["hookified@1.12.1", "", {}, "sha512-xnKGl+iMIlhrZmGHB729MqlmPoWBznctSQTYCpFKqNsCgimJQmithcW0xSQMMFzYnV2iKUh25alswn6epgxS0Q=="],
|
||||
"hookified": ["hookified@1.12.2", "", {}, "sha512-aokUX1VdTpI0DUsndvW+OiwmBpKCu/NgRsSSkuSY0zq8PY6Q6a+lmOfAFDXAAOtBqJELvcWY9L1EVtzjbQcMdg=="],
|
||||
|
||||
"html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="],
|
||||
|
||||
@ -2748,7 +2748,7 @@
|
||||
|
||||
"mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="],
|
||||
|
||||
"mock-apollo-client": ["mock-apollo-client@1.3.1", "", { "peerDependencies": { "@apollo/client": "^3.0.0" } }, "sha512-jBl1YGofh9RpTUFfShwIumiry5qRkR1LYW12K1iZ576kMFh03psHTRiuY2k3dT6cUQ28RAK4gRFl9lVloazGhA=="],
|
||||
"mock-apollo-client": ["mock-apollo-client@1.4.0", "", { "peerDependencies": { "@apollo/client": "^3.0.0" } }, "sha512-uUz7p5Oa+Tqgtl4UaeLQ3QkfZb6fl4MBs/R99fKj4u/+Vmzh1vf74z8JQVgvarXZg0RM/oAFGFdqNuRigJFzTA=="],
|
||||
|
||||
"moo": ["moo@0.5.2", "", {}, "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="],
|
||||
|
||||
@ -2802,7 +2802,7 @@
|
||||
|
||||
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.23", "", {}, "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg=="],
|
||||
"node-releases": ["node-releases@2.0.25", "", {}, "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA=="],
|
||||
|
||||
"nodemailer": ["nodemailer@6.10.1", "", {}, "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="],
|
||||
|
||||
@ -2846,7 +2846,7 @@
|
||||
|
||||
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
||||
|
||||
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
"ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
@ -2866,7 +2866,7 @@
|
||||
|
||||
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
||||
|
||||
"oxc-resolver": ["oxc-resolver@11.9.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.9.0", "@oxc-resolver/binding-android-arm64": "11.9.0", "@oxc-resolver/binding-darwin-arm64": "11.9.0", "@oxc-resolver/binding-darwin-x64": "11.9.0", "@oxc-resolver/binding-freebsd-x64": "11.9.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.9.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.9.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.9.0", "@oxc-resolver/binding-linux-arm64-musl": "11.9.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.9.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.9.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.9.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.9.0", "@oxc-resolver/binding-linux-x64-gnu": "11.9.0", "@oxc-resolver/binding-linux-x64-musl": "11.9.0", "@oxc-resolver/binding-wasm32-wasi": "11.9.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.9.0", "@oxc-resolver/binding-win32-ia32-msvc": "11.9.0", "@oxc-resolver/binding-win32-x64-msvc": "11.9.0" } }, "sha512-u714L0DBBXpD0ERErCQlun2XwinuBfIGo2T8bA7xE8WLQ4uaJudO/VOEQCWslOmcDY2nEkS+UVir5PpyvSG23w=="],
|
||||
"oxc-resolver": ["oxc-resolver@11.11.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.11.0", "@oxc-resolver/binding-android-arm64": "11.11.0", "@oxc-resolver/binding-darwin-arm64": "11.11.0", "@oxc-resolver/binding-darwin-x64": "11.11.0", "@oxc-resolver/binding-freebsd-x64": "11.11.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.11.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.11.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.11.0", "@oxc-resolver/binding-linux-arm64-musl": "11.11.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.11.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.11.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.11.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.11.0", "@oxc-resolver/binding-linux-x64-gnu": "11.11.0", "@oxc-resolver/binding-linux-x64-musl": "11.11.0", "@oxc-resolver/binding-wasm32-wasi": "11.11.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.11.0", "@oxc-resolver/binding-win32-ia32-msvc": "11.11.0", "@oxc-resolver/binding-win32-x64-msvc": "11.11.0" } }, "sha512-vVeBJf77zBeqOA/LBCTO/pr0/ETHGSleCRsI5Kmsf2OsfB5opzhhZptt6VxkqjKWZH+eF1se88fYDG5DGRLjkg=="],
|
||||
|
||||
"p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="],
|
||||
|
||||
@ -3020,7 +3020,7 @@
|
||||
|
||||
"punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
|
||||
|
||||
"qified": ["qified@0.5.0", "", { "dependencies": { "hookified": "^1.12.1" } }, "sha512-Zj6Q/Vc/SQ+Fzc87N90jJUzBzxD7MVQ2ZvGyMmYtnl2u1a07CejAhvtk4ZwASos+SiHKCAIylyGHJKIek75QBw=="],
|
||||
"qified": ["qified@0.5.1", "", { "dependencies": { "hookified": "^1.12.2" } }, "sha512-+BtFN3dCP+IaFA6IYNOu/f/uK1B8xD2QWyOeCse0rjtAebBmkzgd2d1OAXi3ikAzJMIBSdzZDNZ3wZKEUDQs5w=="],
|
||||
|
||||
"qrcanvas": ["qrcanvas@3.1.2", "", { "dependencies": { "@babel/runtime": "^7.11.2", "qrcode-generator": "^1.4.4" } }, "sha512-lNcAyCHN0Eno/mJ5eBc7lHV/5ejAJxII0UELthG3bNnlLR+u8hCc7CR+hXBawbYUf96kNIosXfG2cJzx92ZWKg=="],
|
||||
|
||||
@ -3110,7 +3110,7 @@
|
||||
|
||||
"rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="],
|
||||
|
||||
"rollup": ["rollup@4.52.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.4", "@rollup/rollup-android-arm64": "4.52.4", "@rollup/rollup-darwin-arm64": "4.52.4", "@rollup/rollup-darwin-x64": "4.52.4", "@rollup/rollup-freebsd-arm64": "4.52.4", "@rollup/rollup-freebsd-x64": "4.52.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.4", "@rollup/rollup-linux-arm64-gnu": "4.52.4", "@rollup/rollup-linux-arm64-musl": "4.52.4", "@rollup/rollup-linux-loong64-gnu": "4.52.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-musl": "4.52.4", "@rollup/rollup-linux-s390x-gnu": "4.52.4", "@rollup/rollup-linux-x64-gnu": "4.52.4", "@rollup/rollup-linux-x64-musl": "4.52.4", "@rollup/rollup-openharmony-arm64": "4.52.4", "@rollup/rollup-win32-arm64-msvc": "4.52.4", "@rollup/rollup-win32-ia32-msvc": "4.52.4", "@rollup/rollup-win32-x64-gnu": "4.52.4", "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ=="],
|
||||
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
||||
|
||||
"rrweb-cssom": ["rrweb-cssom@0.7.1", "", {}, "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg=="],
|
||||
|
||||
@ -3234,7 +3234,7 @@
|
||||
|
||||
"statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||
|
||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||
|
||||
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
||||
|
||||
@ -3450,7 +3450,7 @@
|
||||
|
||||
"uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
|
||||
|
||||
"udx-native": ["udx-native@1.18.3", "", { "dependencies": { "b4a": "^1.5.0", "bare-events": "^2.2.0", "require-addon": "^1.1.0", "streamx": "^2.22.0" } }, "sha512-GqYA64plvp8+LY20PQvWuS2wY6/UBKLwpWb/po0iPlEYYnyxAqdag1yKeNJJ+HZtV95T4VYxKbT1xV8eV4iTiw=="],
|
||||
"udx-native": ["udx-native@1.19.2", "", { "dependencies": { "b4a": "^1.5.0", "bare-events": "^2.2.0", "require-addon": "^1.1.0", "streamx": "^2.22.0" } }, "sha512-RNYh+UhfryCsF5hE2ZOuIqcZ+qdipXK3UsarwxWJwsUQZFE3ybwz0mPjwb5ev1PMBcjFahWiepS/q0wwL51c2g=="],
|
||||
|
||||
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
||||
|
||||
@ -3474,7 +3474,7 @@
|
||||
|
||||
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"unimport": ["unimport@5.4.1", "", { "dependencies": { "acorn": "^8.15.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.19", "mlly": "^1.8.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "strip-literal": "^3.1.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.10", "unplugin-utils": "^0.3.0" } }, "sha512-wMZ2JKUCleCK2zfRHeWcbrUHKXOC3SVBYkyn/wTGzh0THX6sT4hSjuKXxKANN4/WMbT6ZPM4JzcDcnhD2x9Bpg=="],
|
||||
"unimport": ["unimport@5.5.0", "", { "dependencies": { "acorn": "^8.15.0", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.19", "mlly": "^1.8.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "pkg-types": "^2.3.0", "scule": "^1.3.0", "strip-literal": "^3.1.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.10", "unplugin-utils": "^0.3.0" } }, "sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg=="],
|
||||
|
||||
"universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||
|
||||
@ -3518,7 +3518,7 @@
|
||||
|
||||
"vee-validate": ["vee-validate@4.15.1", "", { "dependencies": { "@vue/devtools-api": "^7.5.2", "type-fest": "^4.8.3" }, "peerDependencies": { "vue": "^3.4.26" } }, "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg=="],
|
||||
|
||||
"vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="],
|
||||
"vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
|
||||
|
||||
"vite-node": ["vite-node@2.1.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA=="],
|
||||
|
||||
@ -3674,6 +3674,8 @@
|
||||
|
||||
"@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"@babel/core/convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
@ -3716,10 +3718,6 @@
|
||||
|
||||
"@intlify/bundle-utils/yaml-eslint-parser": ["yaml-eslint-parser@1.3.0", "", { "dependencies": { "eslint-visitor-keys": "^3.0.0", "yaml": "^2.0.0" } }, "sha512-E/+VitOorXSLiAqtTd7Yqax0/pAS3xaYMP+AUUJGOK1OZG3rhcj9fcJOM5HJ2VrP1FrStVCWr1muTfQCdj4tAA=="],
|
||||
|
||||
"@intlify/core-base/@intlify/shared": ["@intlify/shared@9.14.5", "", {}, "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ=="],
|
||||
|
||||
"@intlify/message-compiler/@intlify/shared": ["@intlify/shared@9.14.5", "", {}, "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ=="],
|
||||
|
||||
"@intlify/unplugin-vue-i18n/@intlify/shared": ["@intlify/shared@11.1.12", "", {}, "sha512-Om86EjuQtA69hdNj3GQec9ZC0L0vPSAnXzB3gP/gyJ7+mA7t06d9aOAiqMZ+xEOsumGP4eEBlfl8zF2LOTzf2A=="],
|
||||
|
||||
"@intlify/vue-i18n-extensions/@intlify/shared": ["@intlify/shared@10.0.8", "", {}, "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw=="],
|
||||
@ -3762,15 +3760,13 @@
|
||||
|
||||
"@jest/source-map/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"@jest/transform/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
||||
|
||||
"@jest/transform/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"@jest/transform/write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="],
|
||||
|
||||
"@jest/types/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@morev/utils/ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="],
|
||||
"@keyv/bigmap/keyv": ["keyv@5.5.3", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A=="],
|
||||
|
||||
"@morev/utils/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
@ -3778,6 +3774,8 @@
|
||||
|
||||
"@nuxt/kit/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"@nuxt/kit/ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"@nuxt/kit/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"@nuxt/kit/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
@ -3824,6 +3822,8 @@
|
||||
|
||||
"@types/serve-static/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@types/serve-static/@types/send": ["@types/send@0.17.5", "", { "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w=="],
|
||||
|
||||
"@types/sodium-native/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@types/source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
@ -3930,6 +3930,8 @@
|
||||
|
||||
"c12/dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
||||
|
||||
"c12/ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||
|
||||
"c12/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"c12/pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||
@ -4294,8 +4296,6 @@
|
||||
|
||||
"unplugin-vue-components/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"v8-to-istanbul/convert-source-map": ["convert-source-map@1.9.0", "", {}, "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="],
|
||||
|
||||
"vee-validate/@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="],
|
||||
|
||||
"vee-validate/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
@ -4320,8 +4320,6 @@
|
||||
|
||||
"vue-apollo/throttle-debounce": ["throttle-debounce@2.3.0", "", {}, "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ=="],
|
||||
|
||||
"vue-i18n/@intlify/core-base": ["@intlify/core-base@9.13.1", "", { "dependencies": { "@intlify/message-compiler": "9.13.1", "@intlify/shared": "9.13.1" } }, "sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w=="],
|
||||
|
||||
"wcwidth/defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="],
|
||||
|
||||
"web-resource-inliner/htmlparser2": ["htmlparser2@5.0.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^3.3.0", "domutils": "^2.4.2", "entities": "^2.0.0" } }, "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ=="],
|
||||
@ -4348,7 +4346,7 @@
|
||||
|
||||
"@hyperswarm/secret-stream/sodium-universal/sodium-native": ["sodium-native@5.0.9", "", { "dependencies": { "require-addon": "^1.1.0", "which-runtime": "^1.2.1" } }, "sha512-6fpu3d6zdrRpLhuV3CDIBO5g90KkgaeR+c3xvDDz0ZnDkAlqbbPhFW7zhMJfsskfZ9SuC3SvBbqvxcECkXRyKw=="],
|
||||
|
||||
"@iconify/utils/@antfu/install-pkg/package-manager-detector": ["package-manager-detector@1.4.0", "", {}, "sha512-rRZ+pR1Usc+ND9M2NkmCvE/LYJS+8ORVV9X0KuNSY/gFsp7RBHJM/ADh9LYq4Vvfq6QkKrW6/weuh8SMEtN5gw=="],
|
||||
"@iconify/utils/@antfu/install-pkg/package-manager-detector": ["package-manager-detector@1.5.0", "", {}, "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw=="],
|
||||
|
||||
"@iconify/utils/@antfu/install-pkg/tinyexec": ["tinyexec@1.0.1", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="],
|
||||
|
||||
@ -4600,8 +4598,6 @@
|
||||
|
||||
"vue-apollo/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||
|
||||
"vue-i18n/@intlify/core-base/@intlify/message-compiler": ["@intlify/message-compiler@9.13.1", "", { "dependencies": { "@intlify/shared": "9.13.1", "source-map-js": "^1.0.2" } }, "sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w=="],
|
||||
|
||||
"vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="],
|
||||
|
||||
"vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="],
|
||||
|
||||
26
database/migration/migrations/0096-upgrade_dlt_tables.ts
Normal file
26
database/migration/migrations/0096-upgrade_dlt_tables.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(`
|
||||
ALTER TABLE \`dlt_transactions\`
|
||||
CHANGE \`transactions_id\` \`transaction_id\` INT(10) UNSIGNED NULL DEFAULT NULL,
|
||||
ADD \`user_id\` INT UNSIGNED NULL DEFAULT NULL AFTER \`transaction_id\`,
|
||||
ADD \`transaction_link_id\` INT UNSIGNED NULL DEFAULT NULL AFTER \`user_id\`,
|
||||
ADD \`type_id\` INT UNSIGNED NOT NULL AFTER \`transaction_link_id\`,
|
||||
ADD \`error\` text NULL DEFAULT NULL AFTER \`verified_at\`
|
||||
;
|
||||
`)
|
||||
}
|
||||
|
||||
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||
await queryFn(`
|
||||
ALTER TABLE \`dlt_transactions\`
|
||||
CHANGE \`transaction_id\` \`transactions_id\` INT(10) UNSIGNED NOT NULL,
|
||||
DROP COLUMN \`user_id\`,
|
||||
DROP COLUMN \`transaction_link_id\`,
|
||||
DROP COLUMN \`type_id\`,
|
||||
DROP COLUMN \`error\`
|
||||
;
|
||||
`)
|
||||
}
|
||||
@ -93,7 +93,7 @@ export class AppDatabase {
|
||||
public async destroy(): Promise<void> {
|
||||
await this.dataSource?.destroy()
|
||||
}
|
||||
|
||||
|
||||
// ######################################
|
||||
// private methods
|
||||
// ######################################
|
||||
|
||||
@ -1,13 +1,24 @@
|
||||
import { BaseEntity, Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Transaction } from './Transaction'
|
||||
import { TransactionLink } from './TransactionLink'
|
||||
import { User } from './User'
|
||||
|
||||
@Entity('dlt_transactions', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
|
||||
export class DltTransaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||
id: number
|
||||
|
||||
@Column({ name: 'transactions_id', type: 'int', unsigned: true, nullable: false })
|
||||
transactionId: number
|
||||
@Column({ name: 'transaction_id', type: 'int', unsigned: true, nullable: true })
|
||||
transactionId?: number | null
|
||||
|
||||
@Column({ name: 'user_id', type: 'int', unsigned: true, nullable: true })
|
||||
userId?: number | null
|
||||
|
||||
@Column({ name: 'transaction_link_id', type: 'int', unsigned: true, nullable: true })
|
||||
transactionLinkId?: number | null
|
||||
|
||||
@Column({ name: 'type_id', type: 'int', unsigned: true, nullable: false })
|
||||
typeId: number
|
||||
|
||||
@Column({
|
||||
name: 'message_id',
|
||||
@ -34,10 +45,27 @@ export class DltTransaction extends BaseEntity {
|
||||
@Column({ name: 'verified_at', type: 'datetime', precision: 3, nullable: true, default: null })
|
||||
verifiedAt: Date | null
|
||||
|
||||
@Column({ name: 'error', type: 'text', nullable: true })
|
||||
error: string | null
|
||||
|
||||
@OneToOne(
|
||||
() => Transaction,
|
||||
(transaction) => transaction.dltTransaction,
|
||||
)
|
||||
@JoinColumn({ name: 'transactions_id' })
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction?: Transaction | null
|
||||
|
||||
@OneToOne(
|
||||
() => User,
|
||||
(user) => user.dltTransaction,
|
||||
)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
user?: User | null
|
||||
|
||||
@OneToOne(
|
||||
() => TransactionLink,
|
||||
(transactionLink) => transactionLink.dltTransaction,
|
||||
)
|
||||
@JoinColumn({ name: 'transaction_link_id' })
|
||||
transactionLink?: TransactionLink | null
|
||||
}
|
||||
|
||||
@ -1,8 +1,17 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { BaseEntity, Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { Contribution } from './Contribution'
|
||||
import { DltTransaction } from './DltTransaction'
|
||||
import { TransactionLink } from './TransactionLink'
|
||||
import { DecimalTransformer } from './transformer/DecimalTransformer'
|
||||
|
||||
@Entity('transactions')
|
||||
@ -168,4 +177,11 @@ export class Transaction extends BaseEntity {
|
||||
@OneToOne(() => Transaction)
|
||||
@JoinColumn({ name: 'previous' })
|
||||
previousTransaction?: Transaction | null
|
||||
|
||||
@ManyToOne(
|
||||
() => TransactionLink,
|
||||
(transactionLink) => transactionLink.transactions,
|
||||
)
|
||||
@JoinColumn({ name: 'transaction_link_id' })
|
||||
transactionLink?: TransactionLink | null
|
||||
}
|
||||
|
||||
@ -1,6 +1,18 @@
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
import { BaseEntity, Column, DeleteDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import {
|
||||
BaseEntity,
|
||||
Column,
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm'
|
||||
import { DltTransaction } from './DltTransaction'
|
||||
import { Transaction } from './Transaction'
|
||||
import { DecimalTransformer } from './transformer/DecimalTransformer'
|
||||
import { User } from './User'
|
||||
|
||||
@Entity('transaction_links')
|
||||
export class TransactionLink extends BaseEntity {
|
||||
@ -58,4 +70,25 @@ export class TransactionLink extends BaseEntity {
|
||||
|
||||
@Column({ type: 'int', unsigned: true, nullable: true })
|
||||
redeemedBy: number | null
|
||||
|
||||
@OneToOne(
|
||||
() => DltTransaction,
|
||||
(dlt) => dlt.transactionLinkId,
|
||||
)
|
||||
@JoinColumn({ name: 'id', referencedColumnName: 'transactionLinkId' })
|
||||
dltTransaction?: DltTransaction | null
|
||||
|
||||
@OneToOne(
|
||||
() => User,
|
||||
(user) => user.transactionLink,
|
||||
)
|
||||
@JoinColumn({ name: 'userId' })
|
||||
user: User
|
||||
|
||||
@OneToMany(
|
||||
() => Transaction,
|
||||
(transaction) => transaction.transactionLink,
|
||||
)
|
||||
@JoinColumn({ referencedColumnName: 'transaction_link_id' })
|
||||
transactions: Transaction[]
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ import {
|
||||
import { Community } from './Community'
|
||||
import { Contribution } from './Contribution'
|
||||
import { ContributionMessage } from './ContributionMessage'
|
||||
import { DltTransaction } from './DltTransaction'
|
||||
import { TransactionLink } from './TransactionLink'
|
||||
import { UserContact } from './UserContact'
|
||||
import { UserRole } from './UserRole'
|
||||
import { GeometryTransformer } from './transformer/GeometryTransformer'
|
||||
@ -213,4 +215,18 @@ export class User extends BaseEntity {
|
||||
)
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
userContacts?: UserContact[]
|
||||
|
||||
@OneToOne(
|
||||
() => DltTransaction,
|
||||
(dlt) => dlt.userId,
|
||||
)
|
||||
@JoinColumn({ name: 'id', referencedColumnName: 'userId' })
|
||||
dltTransaction?: DltTransaction | null
|
||||
|
||||
@OneToOne(
|
||||
() => TransactionLink,
|
||||
(transactionLink) => transactionLink.userId,
|
||||
)
|
||||
@JoinColumn({ name: 'id', referencedColumnName: 'userId' })
|
||||
transactionLink?: TransactionLink | null
|
||||
}
|
||||
|
||||
34
database/src/logging/TransactionLinkLogging.view.ts
Normal file
34
database/src/logging/TransactionLinkLogging.view.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { TransactionLink } from '../entity/TransactionLink'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
|
||||
import { TransactionLoggingView } from './TransactionLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class TransactionLinkLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: TransactionLink) {
|
||||
super()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
userId: this.self.userId,
|
||||
amount: this.decimalToString(this.self.amount),
|
||||
holdAvailableAmount: this.decimalToString(this.self.holdAvailableAmount),
|
||||
memoLength: this.self.memo.length,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
deletedAt: this.dateToString(this.self.deletedAt),
|
||||
validUntil: this.dateToString(this.self.validUntil),
|
||||
redeemedAt: this.dateToString(this.self.redeemedAt),
|
||||
redeemedBy: this.self.redeemedBy,
|
||||
dltTransaction: this.self.dltTransaction
|
||||
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
|
||||
: undefined,
|
||||
user: this.self.user ? new UserLoggingView(this.self.user).toJSON() : undefined,
|
||||
transactions: this.self.transactions.forEach(
|
||||
(transaction) => new TransactionLoggingView(transaction),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { Transaction } from '../entity'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { DltTransactionLoggingView } from './DltTransactionLogging.view'
|
||||
import { TransactionLinkLoggingView } from './TransactionLinkLogging.view'
|
||||
|
||||
// TODO: move enum into database, maybe rename database
|
||||
enum TransactionTypeId {
|
||||
@ -41,7 +42,7 @@ export class TransactionLoggingView extends AbstractLoggingView {
|
||||
linkedUserName: this.self.linkedUserName?.substring(0, 3) + '...',
|
||||
linkedTransactionId: this.self.linkedTransactionId,
|
||||
contribution: this.self.contribution
|
||||
? new ContributionLoggingView(this.self.contribution)
|
||||
? new ContributionLoggingView(this.self.contribution).toJSON()
|
||||
: undefined,
|
||||
dltTransaction: this.self.dltTransaction
|
||||
? new DltTransactionLoggingView(this.self.dltTransaction).toJSON()
|
||||
@ -49,6 +50,9 @@ export class TransactionLoggingView extends AbstractLoggingView {
|
||||
previousTransaction: this.self.previousTransaction
|
||||
? new TransactionLoggingView(this.self.previousTransaction).toJSON()
|
||||
: undefined,
|
||||
transactionLink: this.self.transactionLink
|
||||
? new TransactionLinkLoggingView(this.self.transactionLink).toJSON()
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,10 @@ enum OptInType {
|
||||
}
|
||||
|
||||
export class UserContactLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserContact) {
|
||||
public constructor(
|
||||
private self: UserContact,
|
||||
private showUser = true,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@ -16,9 +19,10 @@ export class UserContactLoggingView extends AbstractLoggingView {
|
||||
return {
|
||||
id: this.self.id,
|
||||
type: this.self.type,
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
user:
|
||||
this.showUser && this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
email: this.self.email?.substring(0, 3) + '...',
|
||||
emailVerificationCode: this.self.emailVerificationCode?.substring(0, 4) + '...',
|
||||
emailOptInTypeId: OptInType[this.self.emailOptInTypeId],
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { User } from '../entity'
|
||||
import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { CommunityLoggingView } from './CommunityLogging.view'
|
||||
import { ContributionLoggingView } from './ContributionLogging.view'
|
||||
import { ContributionMessageLoggingView } from './ContributionMessageLogging.view'
|
||||
import { UserContactLoggingView } from './UserContactLogging.view'
|
||||
@ -21,10 +22,12 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
id: this.self.id,
|
||||
foreign: this.self.foreign,
|
||||
gradidoID: this.self.gradidoID,
|
||||
communityUuid: this.self.communityUuid,
|
||||
community: this.self.community
|
||||
? new CommunityLoggingView(this.self.community).toJSON()
|
||||
: { id: this.self.communityUuid },
|
||||
alias: this.self.alias?.substring(0, 3) + '...',
|
||||
emailContact: this.self.emailContact
|
||||
? new UserContactLoggingView(this.self.emailContact).toJSON()
|
||||
? new UserContactLoggingView(this.self.emailContact, false).toJSON()
|
||||
: { id: this.self.emailId },
|
||||
firstName: this.self.firstName?.substring(0, 3) + '...',
|
||||
lastName: this.self.lastName?.substring(0, 3) + '...',
|
||||
@ -35,7 +38,7 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
hideAmountGDD: this.self.hideAmountGDD,
|
||||
hideAmountGDT: this.self.hideAmountGDT,
|
||||
userRoles: this.self.userRoles
|
||||
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole).toJSON())
|
||||
? this.self.userRoles.map((userRole) => new UserRoleLoggingView(userRole, false).toJSON())
|
||||
: undefined,
|
||||
referrerId: this.self.referrerId,
|
||||
contributionLinkId: this.self.contributionLinkId,
|
||||
@ -50,7 +53,7 @@ export class UserLoggingView extends AbstractLoggingView {
|
||||
: undefined,
|
||||
userContacts: this.self.userContacts
|
||||
? this.self.userContacts.map((userContact) =>
|
||||
new UserContactLoggingView(userContact).toJSON(),
|
||||
new UserContactLoggingView(userContact, false).toJSON(),
|
||||
)
|
||||
: undefined,
|
||||
}
|
||||
|
||||
@ -3,16 +3,20 @@ import { AbstractLoggingView } from './AbstractLogging.view'
|
||||
import { UserLoggingView } from './UserLogging.view'
|
||||
|
||||
export class UserRoleLoggingView extends AbstractLoggingView {
|
||||
public constructor(private self: UserRole) {
|
||||
public constructor(
|
||||
private self: UserRole,
|
||||
private showUser = true,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
public toJSON(): any {
|
||||
return {
|
||||
id: this.self.id,
|
||||
user: this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
user:
|
||||
this.showUser && this.self.user
|
||||
? new UserLoggingView(this.self.user).toJSON()
|
||||
: { id: this.self.userId },
|
||||
role: this.self.role,
|
||||
createdAt: this.dateToString(this.self.createdAt),
|
||||
updatedAt: this.dateToString(this.self.updatedAt),
|
||||
|
||||
@ -76,7 +76,7 @@ export async function getReachableCommunities(
|
||||
{
|
||||
authenticatedAt: Not(IsNull()),
|
||||
federatedCommunities: {
|
||||
verifiedAt: MoreThanOrEqual(new Date(Date.now() - authenticationTimeoutMs))
|
||||
verifiedAt: MoreThanOrEqual(new Date(Date.now() - authenticationTimeoutMs)),
|
||||
}
|
||||
},
|
||||
{ foreign: false },
|
||||
|
||||
15
database/src/queries/events.ts
Normal file
15
database/src/queries/events.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ContributionLink as DbContributionLink, Event as DbEvent, User as DbUser } from '../entity'
|
||||
|
||||
export async function findModeratorCreatingContributionLink(
|
||||
contributionLink: DbContributionLink,
|
||||
): Promise<DbUser | undefined> {
|
||||
const event = await DbEvent.findOne({
|
||||
where: {
|
||||
involvedContributionLinkId: contributionLink.id,
|
||||
// todo: move event types into db
|
||||
type: 'ADMIN_CONTRIBUTION_LINK_CREATE',
|
||||
},
|
||||
relations: { actingUser: true },
|
||||
})
|
||||
return event?.actingUser
|
||||
}
|
||||
@ -2,6 +2,7 @@ import { LOG4JS_BASE_CATEGORY_NAME } from '../config/const'
|
||||
|
||||
export * from './user'
|
||||
export * from './communities'
|
||||
export * from './events'
|
||||
export * from './pendingTransactions'
|
||||
export * from './transactions'
|
||||
export * from './transactionLinks'
|
||||
|
||||
@ -135,6 +135,6 @@ describe('user.queries', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
@ -11,6 +11,17 @@ export async function aliasExists(alias: string): Promise<boolean> {
|
||||
return user !== null
|
||||
}
|
||||
|
||||
export async function getUserById(
|
||||
id: number,
|
||||
withCommunity: boolean = false,
|
||||
withEmailContact: boolean = false,
|
||||
): Promise<DbUser> {
|
||||
return DbUser.findOneOrFail({
|
||||
where: { id },
|
||||
relations: { community: withCommunity, emailContact: withEmailContact },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param identifier could be gradidoID, alias or email of user
|
||||
|
||||
@ -88,6 +88,7 @@ GDT_ACTIVE=false
|
||||
# DLT-Connector (still in develop)
|
||||
DLT_CONNECTOR=false
|
||||
DLT_CONNECTOR_PORT=6010
|
||||
DLT_GRADIDO_NODE_SERVER_HOME_FOLDER=/home/gradido/.gradido
|
||||
|
||||
# used for combining a newsletter on klicktipp with this gradido community
|
||||
# if used, user will be subscribed on register and can unsubscribe in his account
|
||||
|
||||
@ -9,18 +9,16 @@ IOTA_API_URL=https://chrysalis-nodes.iota.org
|
||||
IOTA_COMMUNITY_ALIAS=GRADIDO: TestHelloWelt2
|
||||
IOTA_HOME_COMMUNITY_SEED=aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_dlt
|
||||
DB_DATABASE_TEST=gradido_dlt_test
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=6010
|
||||
|
||||
# Gradido Node Server URL
|
||||
DLT_NODE_SERVER_PORT=8340
|
||||
|
||||
# Gradido Blockchain
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET=21ffbbc616fe
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY=a51ef8ac7ef1abf162fb7a65261acd7a
|
||||
|
||||
# Route to Backend
|
||||
BACKEND_SERVER_URL=http://localhost:4000
|
||||
PORT=4000
|
||||
JWT_SECRET=secret123
|
||||
@ -1,23 +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
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USER=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=gradido_dlt
|
||||
DB_DATABASE_TEST=$DB_DATABASE_TEST
|
||||
TYPEORM_LOGGING_RELATIVE_PATH=typeorm.backend.log
|
||||
|
||||
# DLT-Connector
|
||||
DLT_CONNECTOR_PORT=$DLT_CONNECTOR_PORT
|
||||
|
||||
# Route to Backend
|
||||
BACKEND_SERVER_URL=http://localhost:4000
|
||||
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
**/*.min.js
|
||||
build
|
||||
coverage
|
||||
@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
2
dlt-connector/.gitignore
vendored
2
dlt-connector/.gitignore
vendored
@ -2,7 +2,9 @@
|
||||
/.env
|
||||
/.env.bak
|
||||
/build/
|
||||
/locales/
|
||||
package-json.lock
|
||||
coverage
|
||||
# emacs
|
||||
*~
|
||||
gradido_node
|
||||
@ -1 +0,0 @@
|
||||
v19.5.0
|
||||
@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
printWidth: 100,
|
||||
singleQuote: true,
|
||||
trailingComma: "all",
|
||||
tabWidth: 2,
|
||||
bracketSpacing: true,
|
||||
endOfLine: "auto",
|
||||
};
|
||||
@ -1 +0,0 @@
|
||||
declare module 'bip32-ed25519'
|
||||
@ -1,8 +1,10 @@
|
||||
##################################################################################
|
||||
# BASE ###########################################################################
|
||||
##################################################################################
|
||||
FROM node:19.5.0-alpine3.17 as base
|
||||
#FROM ubuntu:latest as base
|
||||
#FROM node:18.20.7-bookworm-slim as base
|
||||
FROM oven/bun:1.3.0-slim as base
|
||||
#FROM node:18.20.7-alpine3.21 as base
|
||||
# change to alpine after sodium-native ship with native alpine build
|
||||
|
||||
# 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
|
||||
@ -14,14 +16,16 @@ 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"
|
||||
ENV NODE_ENV=production
|
||||
## App relevant Envs
|
||||
ENV PORT="6010"
|
||||
ENV PORT="4000"
|
||||
## Timezone
|
||||
ENV TZ=UTC
|
||||
|
||||
# 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.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"
|
||||
@ -32,9 +36,9 @@ 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++
|
||||
## install: git
|
||||
#RUN apk --no-cache add git
|
||||
|
||||
|
||||
# Settings
|
||||
## Expose Container Port
|
||||
@ -44,42 +48,40 @@ EXPOSE ${PORT}
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
RUN mkdir -p /dlt-database
|
||||
##################################################################################
|
||||
# BUN ############################################################################
|
||||
##################################################################################
|
||||
#FROM base as bun-base
|
||||
|
||||
#RUN apt update && apt install -y --no-install-recommends ca-certificates curl bash unzip
|
||||
#COPY .bun-version .bun-version
|
||||
#RUN apk update && apk add --no-cache curl tar bash
|
||||
#RUN BUN_VERSION=$(cat .bun-version) && \
|
||||
# curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
|
||||
# Add bun's global bin directory to PATH
|
||||
#ENV PATH="/root/.bun/bin:${PATH}"
|
||||
|
||||
##################################################################################
|
||||
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
||||
# Development ####################################################################
|
||||
##################################################################################
|
||||
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
|
||||
FROM base AS development
|
||||
|
||||
# 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"
|
||||
CMD /bin/sh -c "cd dlt-connector && bun install --no-cache --frozen-lockfile && bun dev"
|
||||
|
||||
##################################################################################
|
||||
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||
# Basic Image with bun setup and project and source code #########################
|
||||
##################################################################################
|
||||
FROM base as build
|
||||
FROM base as base-src
|
||||
COPY --chown=app:app ./dlt-connector ./dlt-connector
|
||||
|
||||
# Copy everything from dlt-connector
|
||||
COPY ./dlt-connector/ ./
|
||||
# Copy everything from dlt-database
|
||||
COPY ./dlt-database/ ../dlt-database/
|
||||
##################################################################################
|
||||
# Build ##########################################################################
|
||||
##################################################################################
|
||||
FROM base-src as build
|
||||
|
||||
# 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
|
||||
RUN cd dlt-connector && bun install --no-cache --frozen-lockfile
|
||||
RUN cd dlt-connector && bun typecheck && bun run build
|
||||
|
||||
##################################################################################
|
||||
# TEST ###########################################################################
|
||||
@ -87,33 +89,31 @@ RUN cd ../dlt-database && yarn run build
|
||||
FROM build as test
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run start"
|
||||
CMD /bin/sh -c "cd dlt-connector && bun test"
|
||||
|
||||
##################################################################################
|
||||
# install only node modules needed for running bundle ############################
|
||||
##################################################################################
|
||||
FROM base-src as production-node-modules
|
||||
|
||||
COPY ./scripts ./scripts
|
||||
# add node_modules from production_node_modules
|
||||
RUN cd dlt-connector && bun install --production --frozen-lockfile --no-cache \
|
||||
&& rm -rf /tmp/* ~/.cache node_modules/.cache \
|
||||
&& ../scripts/clean-prebuilds.sh
|
||||
|
||||
##################################################################################
|
||||
# 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 --chown=app:app --from=build ${DOCKER_WORKDIR}/dlt-connector/build/index.js ./index.js
|
||||
# add node_modules from production_node_modules
|
||||
COPY --chown=app:app --from=production-node-modules ${DOCKER_WORKDIR}/dlt-connector/node_modules ./node_modules
|
||||
|
||||
# Copy run scripts run/
|
||||
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
|
||||
COPY ./dlt-connector/.env .
|
||||
COPY ./dlt-connector/log4js-config.json .
|
||||
|
||||
# Run command
|
||||
CMD /bin/sh -c "yarn run start"
|
||||
CMD ["bun", "index.js"]
|
||||
|
||||
62
dlt-connector/README.md
Normal file
62
dlt-connector/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# DLT Connector
|
||||
|
||||
## Overview
|
||||
|
||||
This implements the **DLT Connector** using [gradido-blockchain-js](https://github.com/gradido/gradido-blockchain-js) as a Node module.
|
||||
[gradido-blockchain-js](https://github.com/gradido/gradido-blockchain-js) builds the native library [gradido_blockchain](https://github.com/gradido/gradido_blockchain) via SWIG, making it accessible to Node.js.
|
||||
|
||||
Most of the Logic is handled by gradido-blockchain.
|
||||
The connector’s purpose is to send Gradido transactions, serialized in blockchain format, through the Hiero SDK into the Hedera Network as Topic Messages.
|
||||
The [gradido-node](https://github.com/gradido/gradido_node) listens to these Hedera/Hiero topics, validates the transactions, and stores them efficiently.
|
||||
Transactions can then be retrieved via a JSON-RPC 2.0 API from [gradido-node](https://github.com/gradido/gradido_node)
|
||||
|
||||
---
|
||||
|
||||
## Structure
|
||||
|
||||
The module makes extensive use of schema validation with [Valibot](https://valibot.dev/guides/introduction/).
|
||||
All objects without internal logic are represented as Valibot schemas, with their corresponding TypeScript types inferred automatically.
|
||||
Valibot allows clear separation of *input* and *output* types, reducing the need for repetitive `null | undefined` checks.
|
||||
When a function expects an output type, TypeScript ensures that the data has been validated via `parse` or `safeParse`, guaranteeing type safety at runtime.
|
||||
|
||||
---
|
||||
|
||||
### `src/bootstrap`
|
||||
Contains initialization code executed once at program startup by `src/index.ts`.
|
||||
|
||||
### `src/cache`
|
||||
Contains code for caching expensive computations or remote data.
|
||||
Currently used only by `KeyPairCacheManager`.
|
||||
|
||||
### `src/client`
|
||||
Contains the client implementations for communication with
|
||||
[`gradido-node`](https://github.com/gradido/gradido_node), the backend, and the Hiero service.
|
||||
Each `<Name>Client` class is a singleton providing:
|
||||
- configuration and connection management
|
||||
- API-call methods mirroring the target service
|
||||
Each client may include optional `input.schema.ts` and/or `output.schema.ts` files defining Valibot schemas for its complex data structures.
|
||||
|
||||
### `src/config`
|
||||
Contains the Valibot-based configuration schema, default values, and logic for parsing `process.env`.
|
||||
If a required field is missing or invalid, the module prints an error and terminates the process.
|
||||
|
||||
### `src/data`
|
||||
Contains DCI (Data-Context-Interaction) Data Objects:
|
||||
simple domain objects that are difficult to express as Valibot schemas, as well as Logic Objects containing core business logic.
|
||||
Also includes domain enums.
|
||||
|
||||
### `src/interactions`
|
||||
Contains complex business logic (Interactions in DCI terms).
|
||||
Each use case resides in its own subfolder with one Context Object and corresponding Role Objects.
|
||||
|
||||
### `src/schemas`
|
||||
Contains Valibot schemas shared across multiple parts of the program.
|
||||
|
||||
### `src/server`
|
||||
Contains the [Elysia](https://elysiajs.com/at-glance.html)-based REST API used by the backend to submit new Gradido transactions.
|
||||
It is intended to integrate with Valibot schemas; currently, it uses `@sinclair/typebox` to convert Valibot schemas to the native format expected by Elysia.
|
||||
|
||||
### `src/utils`
|
||||
Contains small, generic helper functions that do not clearly fit into any of the other directories.
|
||||
|
||||
---
|
||||
154
dlt-connector/biome.json
Normal file
154
dlt-connector/biome.json
Normal file
@ -0,0 +1,154 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
||||
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
|
||||
"files": {
|
||||
"ignoreUnknown": false,
|
||||
"includes": [
|
||||
"**/package.json",
|
||||
"src/**/*.js",
|
||||
"src/**/*.ts",
|
||||
"!**/build",
|
||||
"!**/node_modules",
|
||||
"!**/coverage"
|
||||
]
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"useEditorconfig": true,
|
||||
"formatWithErrors": false,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineEnding": "lf",
|
||||
"lineWidth": 100,
|
||||
"attributePosition": "auto",
|
||||
"bracketSpacing": true
|
||||
},
|
||||
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": false,
|
||||
"complexity": {
|
||||
"noExtraBooleanCast": "error",
|
||||
"noUselessCatch": "error",
|
||||
"noUselessConstructor": "error",
|
||||
"noUselessLoneBlockStatements": "error",
|
||||
"noUselessRename": "error",
|
||||
"noUselessTernary": "error",
|
||||
"noUselessUndefinedInitialization": "error",
|
||||
"noVoid": "error",
|
||||
"useLiteralKeys": "error",
|
||||
"useRegexLiterals": "error",
|
||||
"noAdjacentSpacesInRegex": "error",
|
||||
"noCommaOperator": "error"
|
||||
},
|
||||
"correctness": {
|
||||
"noConstAssign": "error",
|
||||
"noConstantCondition": "error",
|
||||
"noEmptyCharacterClassInRegex": "error",
|
||||
"noEmptyPattern": "error",
|
||||
"noGlobalObjectCalls": "error",
|
||||
"noInnerDeclarations": "error",
|
||||
"noInvalidConstructorSuper": "error",
|
||||
"noInvalidUseBeforeDeclaration": "error",
|
||||
"noNodejsModules": "off",
|
||||
"noNonoctalDecimalEscape": "error",
|
||||
"noPrecisionLoss": "error",
|
||||
"noSelfAssign": "error",
|
||||
"noSetterReturn": "error",
|
||||
"noSwitchDeclarations": "error",
|
||||
"noUndeclaredVariables": "error",
|
||||
"noUnreachable": "error",
|
||||
"noUnreachableSuper": "error",
|
||||
"noUnsafeFinally": "error",
|
||||
"noUnsafeOptionalChaining": "error",
|
||||
"noUnusedLabels": "error",
|
||||
"noUnusedVariables": "error",
|
||||
"useIsNan": "error",
|
||||
"useValidForDirection": "error",
|
||||
"useYield": "error",
|
||||
"noInvalidBuiltinInstantiation": "error",
|
||||
"useValidTypeof": "error"
|
||||
},
|
||||
"security": { "noGlobalEval": "error" },
|
||||
"style": {
|
||||
"noDefaultExport": "error",
|
||||
"noYodaExpression": "error",
|
||||
"useBlockStatements": "error",
|
||||
"useConsistentBuiltinInstantiation": "error",
|
||||
"useConst": "error",
|
||||
"useImportType": "off",
|
||||
"useSingleVarDeclarator": "error",
|
||||
"useArrayLiterals": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noAsyncPromiseExecutor": "error",
|
||||
"noCatchAssign": "error",
|
||||
"noClassAssign": "error",
|
||||
"noCompareNegZero": "error",
|
||||
"noConsole": "error",
|
||||
"noControlCharactersInRegex": "error",
|
||||
"noDebugger": "error",
|
||||
"noDoubleEquals": "error",
|
||||
"noDuplicateCase": "error",
|
||||
"noDuplicateClassMembers": "error",
|
||||
"noDuplicateObjectKeys": "error",
|
||||
"noDuplicateParameters": "error",
|
||||
"noEmptyBlockStatements": "error",
|
||||
"noFallthroughSwitchClause": "error",
|
||||
"noFunctionAssign": "error",
|
||||
"noGlobalAssign": "error",
|
||||
"noImportAssign": "error",
|
||||
"noMisleadingCharacterClass": "error",
|
||||
"noPrototypeBuiltins": "error",
|
||||
"noRedeclare": "error",
|
||||
"noSelfCompare": "error",
|
||||
"noShadowRestrictedNames": "error",
|
||||
"noSparseArray": "error",
|
||||
"noUnsafeNegation": "error",
|
||||
"useDefaultSwitchClauseLast": "error",
|
||||
"useGetterReturn": "error",
|
||||
"noWith": "error",
|
||||
"noVar": "warn"
|
||||
}
|
||||
},
|
||||
"includes": ["**", "!**/node_modules", "!**/*.min.js", "!**/build", "!**/coverage"]
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"jsxQuoteStyle": "single",
|
||||
"quoteProperties": "asNeeded",
|
||||
"trailingCommas": "all",
|
||||
"semicolons": "asNeeded",
|
||||
"arrowParentheses": "always",
|
||||
"bracketSameLine": false,
|
||||
"quoteStyle": "single",
|
||||
"attributePosition": "auto",
|
||||
"bracketSpacing": true
|
||||
},
|
||||
"globals": [
|
||||
"document",
|
||||
"navigator",
|
||||
"window",
|
||||
"describe",
|
||||
"test",
|
||||
"it",
|
||||
"expect",
|
||||
"beforeAll",
|
||||
"beforeEach",
|
||||
"afterAll",
|
||||
"afterEach",
|
||||
"jest"
|
||||
],
|
||||
"parser": {
|
||||
"unsafeParameterDecoratorsEnabled": true
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"includes": ["**/*.ts", "**/*.tsx"],
|
||||
"linter": { "rules": { "complexity": { "noVoid": "error" } } }
|
||||
},
|
||||
{ "includes": ["**/*.test.ts"], "linter": { "rules": {} } }
|
||||
]
|
||||
}
|
||||
1090
dlt-connector/bun.lock
Normal file
1090
dlt-connector/bun.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,48 +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',
|
||||
'@typeorm/(.*)': '<rootDir>/src/typeorm/$1',
|
||||
'@client/(.*)': '<rootDir>/src/client/$1',
|
||||
'@entity/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../dlt-database/entity/$1'
|
||||
: '<rootDir>/../dlt-database/build/entity/$1',
|
||||
'@dbTools/(.*)':
|
||||
// eslint-disable-next-line n/no-process-env
|
||||
process.env.NODE_ENV === 'development'
|
||||
? '<rootDir>/../dlt-database/src/$1'
|
||||
: '<rootDir>/../dlt-database/build/src/$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/*"],
|
||||
*/
|
||||
@ -8,7 +8,20 @@
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%f : %l] - %m"
|
||||
},
|
||||
"compress": true,
|
||||
"keepFileExt" : true,
|
||||
"fileNameSep" : "_",
|
||||
"numBackups" : 30
|
||||
},
|
||||
"dlt.client.HieroClient": {
|
||||
"type": "dateFile",
|
||||
"filename": "../logs/dlt-connector/apiversion-%v.log",
|
||||
"pattern": "yyyy-MM-dd",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [topicId=%X{topicId}] [%f : %l] - %m"
|
||||
},
|
||||
"compress": true,
|
||||
"keepFileExt" : true,
|
||||
@ -22,7 +35,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 [%f : %l] - %m"
|
||||
},
|
||||
"compress": true,
|
||||
"keepFileExt" : true,
|
||||
@ -40,7 +53,7 @@
|
||||
"type": "stdout",
|
||||
"layout":
|
||||
{
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m"
|
||||
"type": "pattern", "pattern": "%d{ISO8601} %p %c [%f : %l] - %m"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,79 +1,46 @@
|
||||
{
|
||||
"name": "gradido-dlt-connector",
|
||||
"name": "dlt-connector",
|
||||
"repository": "git@github.com:gradido/gradido.git",
|
||||
"version": "2.6.1",
|
||||
"description": "Gradido DLT-Connector",
|
||||
"main": "src/index.ts",
|
||||
"repository": "https://github.com/gradido/gradido/",
|
||||
"author": "Dario Rekowski",
|
||||
"author": "Gradido Academy - https://www.gradido.net",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"private": true,
|
||||
"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 --runInBand --forceExit --detectOpenHandles"
|
||||
"start": "bun run src/index.ts",
|
||||
"build": "bun build src/index.ts --outdir=build --target=bun --external=gradido-blockchain-js --minify",
|
||||
"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",
|
||||
"bip32-ed25519": "^0.0.4",
|
||||
"bip39": "^3.1.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"class-validator": "^0.14.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"decimal.js-light": "^2.5.1",
|
||||
"dlt-database": "file:../dlt-database",
|
||||
"dotenv": "10.0.0",
|
||||
"express": "4.17.1",
|
||||
"express-slow-down": "^2.0.1",
|
||||
"graphql": "^16.7.1",
|
||||
"graphql-request": "^6.1.0",
|
||||
"graphql-scalars": "^1.22.2",
|
||||
"helmet": "^7.1.0",
|
||||
"jose": "^5.2.2",
|
||||
"log4js": "^6.7.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"protobufjs": "^7.2.5",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"sodium-native": "^4.0.4",
|
||||
"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"
|
||||
},
|
||||
"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",
|
||||
"@biomejs/biome": "2.0.0",
|
||||
"@hashgraph/sdk": "^2.70.0",
|
||||
"@sinclair/typebox": "^0.34.33",
|
||||
"@sinclair/typemap": "^0.10.1",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/bun": "^1.2.17",
|
||||
"@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",
|
||||
"typeorm": "^0.3.17",
|
||||
"typeorm-extension": "^3.0.1",
|
||||
"typescript": "^4.9.4"
|
||||
"adm-zip": "^0.5.16",
|
||||
"async-mutex": "^0.5.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"elysia": "1.3.8",
|
||||
"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"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
"node": ">=18"
|
||||
},
|
||||
"module": "src/index.js"
|
||||
}
|
||||
|
||||
@ -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!
|
||||
}
|
||||
26
dlt-connector/src/bootstrap/appContext.ts
Normal file
26
dlt-connector/src/bootstrap/appContext.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { KeyPairCacheManager } from '../cache/KeyPairCacheManager'
|
||||
import { BackendClient } from '../client/backend/BackendClient'
|
||||
import { GradidoNodeClient } from '../client/GradidoNode/GradidoNodeClient'
|
||||
import { HieroClient } from '../client/hiero/HieroClient'
|
||||
|
||||
export type AppContextClients = {
|
||||
backend: BackendClient
|
||||
hiero: HieroClient
|
||||
gradidoNode: GradidoNodeClient
|
||||
}
|
||||
|
||||
export type AppContext = {
|
||||
cache: KeyPairCacheManager
|
||||
clients: AppContextClients
|
||||
}
|
||||
|
||||
export function createAppContext(): AppContext {
|
||||
return {
|
||||
cache: KeyPairCacheManager.getInstance(),
|
||||
clients: {
|
||||
backend: BackendClient.getInstance(),
|
||||
hiero: HieroClient.getInstance(),
|
||||
gradidoNode: GradidoNodeClient.getInstance(),
|
||||
},
|
||||
}
|
||||
}
|
||||
95
dlt-connector/src/bootstrap/init.ts
Normal file
95
dlt-connector/src/bootstrap/init.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { loadCryptoKeys, MemoryBlock } from 'gradido-blockchain-js'
|
||||
import { configure, getLogger, Logger } from 'log4js'
|
||||
import * as v from 'valibot'
|
||||
import { CONFIG } from '../config'
|
||||
import { MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE } from '../config/const'
|
||||
import { SendToHieroContext } from '../interactions/sendToHiero/SendToHiero.context'
|
||||
import { Community, communitySchema } from '../schemas/transaction.schema'
|
||||
import { isPortOpenRetry } from '../utils/network'
|
||||
import { type AppContext, type AppContextClients } from './appContext'
|
||||
import { initGradidoNode } from './initGradidoNode'
|
||||
|
||||
export function loadConfig(): Logger {
|
||||
// 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')
|
||||
|
||||
// load crypto keys for gradido blockchain lib
|
||||
loadCryptoKeys(
|
||||
MemoryBlock.fromHex(CONFIG.GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET),
|
||||
MemoryBlock.fromHex(CONFIG.GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY),
|
||||
)
|
||||
return logger
|
||||
}
|
||||
|
||||
export async function checkHieroAccount(logger: Logger, clients: AppContextClients): Promise<void> {
|
||||
const balance = await clients.hiero.getBalance()
|
||||
logger.info(`Hiero Account Balance: ${balance.hbars.toString()}`)
|
||||
}
|
||||
|
||||
export async function checkHomeCommunity(
|
||||
appContext: AppContext,
|
||||
logger: Logger,
|
||||
): Promise<Community> {
|
||||
const { backend, hiero } = appContext.clients
|
||||
|
||||
// wait for backend server
|
||||
await isPortOpenRetry(backend.url)
|
||||
// ask backend for home community
|
||||
let homeCommunity = await backend.getHomeCommunityDraft()
|
||||
// on missing topicId, create one
|
||||
if (!homeCommunity.hieroTopicId) {
|
||||
const topicId = await hiero.createTopic(homeCommunity.name)
|
||||
// update topic on backend server
|
||||
homeCommunity = await backend.setHomeCommunityTopicId(homeCommunity.uuid, topicId)
|
||||
} else {
|
||||
// if topic exist, check if we need to update it
|
||||
let topicInfo = await hiero.getTopicInfo(homeCommunity.hieroTopicId)
|
||||
// console.log(`topicInfo: ${JSON.stringify(topicInfo, null, 2)}`)
|
||||
if (
|
||||
topicInfo.expirationTime.getTime() - new Date().getTime() <
|
||||
MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE
|
||||
) {
|
||||
await hiero.updateTopic(homeCommunity.hieroTopicId)
|
||||
topicInfo = await hiero.getTopicInfo(homeCommunity.hieroTopicId)
|
||||
logger.info(
|
||||
`updated topic info, new expiration time: ${topicInfo.expirationTime.toLocaleDateString()}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!homeCommunity.hieroTopicId) {
|
||||
throw new Error('still no topic id, after creating topic and update community in backend.')
|
||||
}
|
||||
appContext.cache.setHomeCommunityTopicId(homeCommunity.hieroTopicId)
|
||||
logger.info(`home community topic: ${homeCommunity.hieroTopicId}`)
|
||||
logger.info(`gradido node server: ${appContext.clients.gradidoNode.url}`)
|
||||
logger.info(`gradido backend server: ${appContext.clients.backend.url}`)
|
||||
return v.parse(communitySchema, homeCommunity)
|
||||
}
|
||||
|
||||
export async function checkGradidoNode(
|
||||
clients: AppContextClients,
|
||||
logger: Logger,
|
||||
homeCommunity: Community,
|
||||
): Promise<void> {
|
||||
// check if gradido node is running, if not setup and start it
|
||||
await initGradidoNode(clients)
|
||||
|
||||
// ask gradido node if community blockchain was created
|
||||
try {
|
||||
if (
|
||||
!(await clients.gradidoNode.getTransaction({
|
||||
transactionId: 1,
|
||||
topic: homeCommunity.hieroTopicId,
|
||||
}))
|
||||
) {
|
||||
// if not exist, create community root transaction
|
||||
await SendToHieroContext(homeCommunity)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`error requesting gradido node: ${e}`)
|
||||
}
|
||||
}
|
||||
89
dlt-connector/src/bootstrap/initGradidoNode.ts
Normal file
89
dlt-connector/src/bootstrap/initGradidoNode.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import AdmZip from 'adm-zip'
|
||||
import { getLogger } from 'log4js'
|
||||
import { exportCommunities } from '../client/GradidoNode/communities'
|
||||
import { GradidoNodeProcess } from '../client/GradidoNode/GradidoNodeProcess'
|
||||
import { HieroClient } from '../client/hiero/HieroClient'
|
||||
import { CONFIG } from '../config'
|
||||
import {
|
||||
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||
GRADIDO_NODE_RUNTIME_PATH,
|
||||
LOG4JS_BASE_CATEGORY,
|
||||
} from '../config/const'
|
||||
import { checkFileExist, checkPathExist } from '../utils/filesystem'
|
||||
import { isPortOpen } from '../utils/network'
|
||||
import { AppContextClients } from './appContext'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.bootstrap.initGradidoNode`)
|
||||
|
||||
export async function initGradidoNode(clients: AppContextClients): Promise<void> {
|
||||
const url = `http://localhost:${CONFIG.DLT_NODE_SERVER_PORT}`
|
||||
const isOpen = await isPortOpen(url)
|
||||
if (isOpen) {
|
||||
logger.info(`GradidoNode is already running on ${url}`)
|
||||
return
|
||||
}
|
||||
|
||||
const gradidoNodeHomeFolder = path.join(
|
||||
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||
)
|
||||
// check folder, create when missing
|
||||
checkPathExist(gradidoNodeHomeFolder, true)
|
||||
|
||||
await Promise.all([
|
||||
// write Hedera Address Book
|
||||
exportHederaAddressbooks(gradidoNodeHomeFolder, clients.hiero),
|
||||
// check GradidoNode Runtime, download when missing
|
||||
ensureGradidoNodeRuntimeAvailable(GRADIDO_NODE_RUNTIME_PATH),
|
||||
// export communities to GradidoNode Folder
|
||||
exportCommunities(gradidoNodeHomeFolder, clients.backend),
|
||||
])
|
||||
GradidoNodeProcess.getInstance().start()
|
||||
}
|
||||
|
||||
async function exportHederaAddressbooks(
|
||||
homeFolder: string,
|
||||
hieroClient: HieroClient,
|
||||
): Promise<void> {
|
||||
const networkName = CONFIG.HIERO_HEDERA_NETWORK
|
||||
const addressBook = await hieroClient.downloadAddressBook()
|
||||
const addressBookPath = path.join(homeFolder, 'addressbook', `${networkName}.pb`)
|
||||
checkPathExist(path.dirname(addressBookPath), true)
|
||||
fs.writeFileSync(addressBookPath, addressBook.toBytes())
|
||||
}
|
||||
|
||||
async function ensureGradidoNodeRuntimeAvailable(runtimeFileName: string): Promise<void> {
|
||||
const runtimeFolder = path.dirname(runtimeFileName)
|
||||
checkPathExist(runtimeFolder, true)
|
||||
if (!checkFileExist(runtimeFileName)) {
|
||||
const runtimeArchiveFilename = createGradidoNodeRuntimeArchiveFilename()
|
||||
const downloadUrl = new URL(
|
||||
`https://github.com/gradido/gradido_node/releases/download/v${CONFIG.DLT_GRADIDO_NODE_SERVER_VERSION}/${runtimeArchiveFilename}`,
|
||||
)
|
||||
logger.debug(`download GradidoNode Runtime from ${downloadUrl}`)
|
||||
const archive = await fetch(downloadUrl)
|
||||
if (!archive.ok) {
|
||||
throw new Error(`Failed to download GradidoNode Runtime: ${archive.statusText}`)
|
||||
}
|
||||
const compressedBuffer = await archive.arrayBuffer()
|
||||
if (process.platform === 'win32') {
|
||||
const zip = new AdmZip(Buffer.from(compressedBuffer))
|
||||
zip.extractAllTo(runtimeFolder, true)
|
||||
} else {
|
||||
const archivePath = path.join(runtimeFolder, runtimeArchiveFilename)
|
||||
logger.debug(`GradidoNode Runtime Archive: ${archivePath}`)
|
||||
fs.writeFileSync(archivePath, Buffer.from(compressedBuffer))
|
||||
execSync(`tar -xzf ${archivePath}`, { cwd: runtimeFolder })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createGradidoNodeRuntimeArchiveFilename(): string {
|
||||
const version = CONFIG.DLT_GRADIDO_NODE_SERVER_VERSION
|
||||
const platform: string = process.platform
|
||||
const fileEnding = platform === 'win32' ? 'zip' : 'tar.gz'
|
||||
return `gradido_node-v${version}-${platform}-${process.arch}.${fileEnding}`
|
||||
}
|
||||
30
dlt-connector/src/bootstrap/shutdown.ts
Normal file
30
dlt-connector/src/bootstrap/shutdown.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Logger } from 'log4js'
|
||||
import { GradidoNodeProcess } from '../client/GradidoNode/GradidoNodeProcess'
|
||||
import { type AppContextClients } from './appContext'
|
||||
|
||||
export function setupGracefulShutdown(logger: Logger, clients: AppContextClients) {
|
||||
const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM']
|
||||
signals.forEach((sig) => {
|
||||
process.on(sig, async () => {
|
||||
logger.info(`[shutdown] Got ${sig}, cleaning up…`)
|
||||
await gracefulShutdown(logger, clients)
|
||||
process.exit(0)
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
const rl = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
})
|
||||
rl.on('SIGINT', () => {
|
||||
process.emit('SIGINT' as any)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function gracefulShutdown(logger: Logger, clients: AppContextClients) {
|
||||
logger.info('graceful shutdown')
|
||||
await clients.hiero.waitForPendingPromises()
|
||||
await GradidoNodeProcess.getInstance().exit()
|
||||
}
|
||||
79
dlt-connector/src/cache/KeyPairCacheManager.ts
vendored
Normal file
79
dlt-connector/src/cache/KeyPairCacheManager.ts
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
import { KeyPairEd25519 } from 'gradido-blockchain-js'
|
||||
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { LOG4JS_BASE_CATEGORY } from '../config/const'
|
||||
import { HieroId } from '../schemas/typeGuard.schema'
|
||||
|
||||
// Source: https://refactoring.guru/design-patterns/singleton/typescript/example
|
||||
// and ../federation/client/FederationClientFactory.ts
|
||||
// TODO: TTL (time to live) based, maybe even optional use of redis
|
||||
/**
|
||||
* A Singleton class defines the `getInstance` method that lets clients access
|
||||
* the unique singleton instance.
|
||||
*/
|
||||
export class KeyPairCacheManager {
|
||||
private static instance: KeyPairCacheManager
|
||||
private cache: Map<string, KeyPairEd25519> = new Map<string, KeyPairEd25519>()
|
||||
private homeCommunityTopicId: HieroId | undefined
|
||||
private logger: Logger
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
private constructor() {
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.KeyPairCacheManager`)
|
||||
}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(): KeyPairCacheManager {
|
||||
if (!KeyPairCacheManager.instance) {
|
||||
KeyPairCacheManager.instance = new KeyPairCacheManager()
|
||||
}
|
||||
return KeyPairCacheManager.instance
|
||||
}
|
||||
|
||||
public setHomeCommunityTopicId(topicId: HieroId): void {
|
||||
this.homeCommunityTopicId = topicId
|
||||
}
|
||||
|
||||
public getHomeCommunityTopicId(): HieroId {
|
||||
if (!this.homeCommunityTopicId) {
|
||||
throw new Error('home community topic id is not set')
|
||||
}
|
||||
return this.homeCommunityTopicId
|
||||
}
|
||||
|
||||
public findKeyPair(input: string): KeyPairEd25519 | undefined {
|
||||
return this.cache.get(input)
|
||||
}
|
||||
|
||||
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(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
|
||||
}
|
||||
}
|
||||
@ -1,105 +0,0 @@
|
||||
/* 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'
|
||||
|
||||
const homeCommunity = gql`
|
||||
query {
|
||||
homeCommunity {
|
||||
uuid
|
||||
foreign
|
||||
creationDate
|
||||
}
|
||||
}
|
||||
`
|
||||
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
|
||||
/**
|
||||
* 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() {}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
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>(
|
||||
homeCommunity,
|
||||
{},
|
||||
{
|
||||
authorization: 'Bearer ' + (await this.createJWTToken()),
|
||||
},
|
||||
)
|
||||
if (errors) {
|
||||
throw new LogError('error getting home community from backend', errors)
|
||||
}
|
||||
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> {
|
||||
const secret = new TextEncoder().encode(CONFIG.JWT_SECRET)
|
||||
const token = await new SignJWT({ gradidoID: 'dlt-connector', 'urn:gradido:claim': true })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setIssuer('urn:gradido:issuer')
|
||||
.setAudience('urn:gradido:audience')
|
||||
.setExpirationTime('1m')
|
||||
.sign(secret)
|
||||
return token
|
||||
}
|
||||
}
|
||||
259
dlt-connector/src/client/GradidoNode/GradidoNodeClient.ts
Normal file
259
dlt-connector/src/client/GradidoNode/GradidoNodeClient.ts
Normal file
@ -0,0 +1,259 @@
|
||||
import { ConfirmedTransaction } from 'gradido-blockchain-js'
|
||||
import JsonRpcClient from 'jsonrpc-ts-client'
|
||||
import { JsonRpcEitherResponse } from 'jsonrpc-ts-client/dist/types/utils/jsonrpc'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import * as v from 'valibot'
|
||||
import { CONFIG } from '../../config'
|
||||
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||
import { AddressType } from '../../data/AddressType.enum'
|
||||
import { Uuidv4Hash } from '../../data/Uuidv4Hash'
|
||||
import { addressTypeSchema, confirmedTransactionSchema } from '../../schemas/typeConverter.schema'
|
||||
import { Hex32, Hex32Input, HieroId, hex32Schema } from '../../schemas/typeGuard.schema'
|
||||
import { isPortOpenRetry } from '../../utils/network'
|
||||
import { GradidoNodeErrorCodes } from './GradidoNodeErrorCodes'
|
||||
import {
|
||||
TransactionIdentifierInput,
|
||||
TransactionsRangeInput,
|
||||
transactionIdentifierSchema,
|
||||
transactionsRangeSchema,
|
||||
} from './input.schema'
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
type WithTimeUsed<T> = T & { timeUsed?: string }
|
||||
|
||||
export class GradidoNodeClient {
|
||||
private static instance: GradidoNodeClient
|
||||
client: JsonRpcClient
|
||||
logger: Logger
|
||||
urlValue: string
|
||||
|
||||
private constructor() {
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNodeClient`)
|
||||
this.urlValue = `http://localhost:${CONFIG.DLT_NODE_SERVER_PORT}/api`
|
||||
this.logger.addContext('url', this.urlValue)
|
||||
this.client = new JsonRpcClient({
|
||||
url: this.urlValue,
|
||||
})
|
||||
}
|
||||
|
||||
public get url(): string {
|
||||
return this.urlValue
|
||||
}
|
||||
|
||||
public static getInstance(): GradidoNodeClient {
|
||||
if (!GradidoNodeClient.instance) {
|
||||
GradidoNodeClient.instance = new GradidoNodeClient()
|
||||
}
|
||||
return GradidoNodeClient.instance
|
||||
}
|
||||
|
||||
/**
|
||||
* getTransaction
|
||||
* get a specific confirmed transaction from a specific community
|
||||
* @param transactionIdentifier
|
||||
* @returns the confirmed transaction or undefined if transaction is not found
|
||||
* @throws GradidoNodeRequestError
|
||||
*/
|
||||
public async getTransaction(
|
||||
transactionIdentifier: TransactionIdentifierInput,
|
||||
): Promise<ConfirmedTransaction | undefined> {
|
||||
const parameter = {
|
||||
...v.parse(transactionIdentifierSchema, transactionIdentifier),
|
||||
format: 'base64',
|
||||
}
|
||||
const response = await this.rpcCall<{ transaction: string }>('getTransaction', parameter)
|
||||
if (response.isSuccess()) {
|
||||
// this.logger.debug('result: ', response.result.transaction)
|
||||
return v.parse(confirmedTransactionSchema, response.result.transaction)
|
||||
}
|
||||
if (response.isError()) {
|
||||
if (response.error.code === GradidoNodeErrorCodes.TRANSACTION_NOT_FOUND) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
throw new GradidoNodeRequestError(response.error.message, response)
|
||||
}
|
||||
|
||||
/**
|
||||
* getLastTransaction
|
||||
* get the last confirmed transaction from a specific community
|
||||
* @param hieroTopic the community hiero topic id
|
||||
* @returns the last confirmed transaction or undefined if blockchain for community is empty or not found
|
||||
* @throws GradidoNodeRequestError
|
||||
*/
|
||||
|
||||
public async getLastTransaction(hieroTopic: HieroId): Promise<ConfirmedTransaction | undefined> {
|
||||
const parameter = {
|
||||
format: 'base64',
|
||||
topic: hieroTopic,
|
||||
}
|
||||
const response = await this.rpcCall<{ transaction: string }>('getLastTransaction', parameter)
|
||||
if (response.isSuccess()) {
|
||||
return v.parse(confirmedTransactionSchema, response.result.transaction)
|
||||
}
|
||||
if (response.isError()) {
|
||||
if (response.error.code === GradidoNodeErrorCodes.GRADIDO_NODE_ERROR) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
throw new GradidoNodeRequestError(response.error.message, response)
|
||||
}
|
||||
|
||||
/**
|
||||
* getTransactions
|
||||
* get list of confirmed transactions from a specific community
|
||||
* @param input fromTransactionId is the id of the first transaction to return
|
||||
* @param input maxResultCount is the max number of transactions to return
|
||||
* @param input topic is the community hiero topic id
|
||||
* @returns list of confirmed transactions
|
||||
* @throws GradidoNodeRequestError
|
||||
* @example
|
||||
* ```
|
||||
* const transactions = await getTransactions({
|
||||
* fromTransactionId: 1,
|
||||
* maxResultCount: 100,
|
||||
* topic: communityUuid,
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async getTransactions(input: TransactionsRangeInput): Promise<ConfirmedTransaction[]> {
|
||||
const parameter = {
|
||||
...v.parse(transactionsRangeSchema, input),
|
||||
format: 'base64',
|
||||
}
|
||||
const result = await this.rpcCallResolved<{ transactions: string[] }>(
|
||||
'getTransactions',
|
||||
parameter,
|
||||
)
|
||||
return result.transactions.map((transactionBase64) =>
|
||||
v.parse(confirmedTransactionSchema, transactionBase64),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* getTransactionsForAccount
|
||||
* get list of confirmed transactions for a specific account
|
||||
* @param transactionRange the range of transactions to return
|
||||
* @param pubkey the public key of the account
|
||||
* @returns list of confirmed transactions
|
||||
* @throws GradidoNodeRequestError
|
||||
*/
|
||||
public async getTransactionsForAccount(
|
||||
transactionRange: TransactionsRangeInput,
|
||||
pubkey: Hex32Input,
|
||||
): Promise<ConfirmedTransaction[]> {
|
||||
const parameter = {
|
||||
...v.parse(transactionsRangeSchema, transactionRange),
|
||||
pubkey: v.parse(hex32Schema, pubkey),
|
||||
format: 'base64',
|
||||
}
|
||||
const response = await this.rpcCallResolved<{ transactions: string[] }>(
|
||||
'getTransactionsForAddress',
|
||||
parameter,
|
||||
)
|
||||
return response.transactions.map((transactionBase64) =>
|
||||
v.parse(confirmedTransactionSchema, transactionBase64),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* getAddressType
|
||||
* get the address type of a specific user
|
||||
* can be used to check if user/account exists on blockchain
|
||||
* look also for gmw, auf and deferred transfer accounts
|
||||
* @param pubkey the public key of the user or account
|
||||
* @param hieroTopic the community hiero topic id
|
||||
* @returns the address type of the user/account, AddressType.NONE if not found
|
||||
* @throws GradidoNodeRequestError
|
||||
*/
|
||||
|
||||
public async getAddressType(pubkey: Hex32Input, hieroTopic: HieroId): Promise<AddressType> {
|
||||
const parameter = {
|
||||
pubkey: v.parse(hex32Schema, pubkey),
|
||||
topic: hieroTopic,
|
||||
}
|
||||
const response = await this.rpcCallResolved<{ addressType: string }>(
|
||||
'getAddressType',
|
||||
parameter,
|
||||
)
|
||||
return v.parse(addressTypeSchema, response.addressType)
|
||||
}
|
||||
|
||||
/**
|
||||
* findUserByNameHash
|
||||
* find a user by name hash
|
||||
* @param nameHash the name hash of the user
|
||||
* @param hieroTopic the community hiero topic id
|
||||
* @returns the public key of the user as hex32 string or undefined if user is not found
|
||||
* @throws GradidoNodeRequestError
|
||||
*/
|
||||
public async findUserByNameHash(
|
||||
nameHash: Uuidv4Hash,
|
||||
hieroTopic: HieroId,
|
||||
): Promise<Hex32 | undefined> {
|
||||
const parameter = {
|
||||
nameHash: nameHash.getAsHexString(),
|
||||
topic: hieroTopic,
|
||||
}
|
||||
const response = await this.rpcCall<{ pubkey: string; timeUsed: string }>(
|
||||
'findUserByNameHash',
|
||||
parameter,
|
||||
)
|
||||
if (response.isSuccess()) {
|
||||
this.logger.info(`call findUserByNameHash, used ${response.result.timeUsed}`)
|
||||
return v.parse(hex32Schema, response.result.pubkey)
|
||||
}
|
||||
if (
|
||||
response.isError() &&
|
||||
response.error.code === GradidoNodeErrorCodes.JSON_RPC_ERROR_ADDRESS_NOT_FOUND
|
||||
) {
|
||||
this.logger.debug(`call findUserByNameHash, return with error: ${response.error.message}`)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// ---------------- intern helper functions -----------------------------------
|
||||
|
||||
// return result on success or throw error
|
||||
protected 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')
|
||||
}
|
||||
|
||||
// template rpcCall, check first if port is open before executing json rpc 2.0 request
|
||||
protected async rpcCall<T>(method: string, parameter: any): Promise<JsonRpcEitherResponse<T>> {
|
||||
this.logger.debug('call %s with %s', method, parameter)
|
||||
await isPortOpenRetry(this.url)
|
||||
return this.client.exec<T>(method, parameter)
|
||||
}
|
||||
|
||||
// template rpcCall, check first if port is open before executing json rpc 2.0 request,
|
||||
// throw error on failure, return result on success
|
||||
protected async rpcCallResolved<T>(method: string, parameter: any): Promise<T> {
|
||||
const response = await this.rpcCall<WithTimeUsed<T>>(method, parameter)
|
||||
return this.resolveResponse(response, (result: WithTimeUsed<T>) => {
|
||||
if (result.timeUsed) {
|
||||
this.logger.info(`call %s, used ${result.timeUsed}`, method)
|
||||
}
|
||||
return result as T
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
}
|
||||
121
dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts
Normal file
121
dlt-connector/src/client/GradidoNode/GradidoNodeProcess.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { Subprocess, spawn } from 'bun'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import { CONFIG } from '../../config'
|
||||
import {
|
||||
GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS,
|
||||
GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS,
|
||||
GRADIDO_NODE_RUNTIME_PATH,
|
||||
LOG4JS_BASE_CATEGORY,
|
||||
} from '../../config/const'
|
||||
|
||||
/**
|
||||
* A Singleton class defines the `getInstance` method that lets clients access
|
||||
* the unique singleton instance.
|
||||
*
|
||||
* Singleton Managing GradidoNode if started as subprocess
|
||||
* will restart GradidoNode if it exits more than `GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS` milliseconds after start
|
||||
* if exit was called, it will first try to exit graceful with SIGTERM and then kill with SIGKILL after `GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS` milliseconds
|
||||
*/
|
||||
export class GradidoNodeProcess {
|
||||
private static instance: GradidoNodeProcess | null = null
|
||||
private proc: Subprocess | null = null
|
||||
private logger: Logger
|
||||
private lastStarted: Date | null = null
|
||||
private exitCalled: boolean = false
|
||||
|
||||
private constructor() {
|
||||
// constructor is private to prevent instantiation from outside
|
||||
// of the class except from the static getInstance method.
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNodeProcess`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method that returns the singleton instance of the class.
|
||||
* @returns the singleton instance of the class.
|
||||
*/
|
||||
public static getInstance(): GradidoNodeProcess {
|
||||
if (!GradidoNodeProcess.instance) {
|
||||
GradidoNodeProcess.instance = new GradidoNodeProcess()
|
||||
}
|
||||
return GradidoNodeProcess.instance
|
||||
}
|
||||
|
||||
public start() {
|
||||
if (this.proc) {
|
||||
this.logger.warn('GradidoNodeProcess already running.')
|
||||
return
|
||||
}
|
||||
this.logger.info(`starting GradidoNodeProcess with path: ${GRADIDO_NODE_RUNTIME_PATH}`)
|
||||
this.lastStarted = new Date()
|
||||
const logger = this.logger
|
||||
this.proc = spawn([GRADIDO_NODE_RUNTIME_PATH], {
|
||||
env: {
|
||||
CLIENTS_HIERO_NETWORKTYPE: CONFIG.HIERO_HEDERA_NETWORK,
|
||||
SERVER_JSON_RPC_PORT: CONFIG.DLT_NODE_SERVER_PORT.toString(),
|
||||
USERPROFILE: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||
HOME: CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||
},
|
||||
onExit(proc, exitCode, signalCode, error) {
|
||||
logger.warn(`GradidoNodeProcess exited with code ${exitCode} and signalCode ${signalCode}`)
|
||||
if (error) {
|
||||
logger.error(`GradidoNodeProcess exit error: ${error}`)
|
||||
/*if (logger.isDebugEnabled() && proc.stderr) {
|
||||
// print error messages from GradidoNode in our own log if debug is enabled
|
||||
proc.stderr
|
||||
.getReader()
|
||||
.read()
|
||||
.then((chunk) => {
|
||||
logger.debug(chunk.value?.toString())
|
||||
})
|
||||
}*/
|
||||
}
|
||||
logger.debug(`ressource usage: ${JSON.stringify(proc?.resourceUsage(), null, 2)}`)
|
||||
const gradidoNodeProcess = GradidoNodeProcess.getInstance()
|
||||
gradidoNodeProcess.proc = null
|
||||
if (
|
||||
!gradidoNodeProcess.exitCalled &&
|
||||
gradidoNodeProcess.lastStarted &&
|
||||
Date.now() - gradidoNodeProcess.lastStarted.getTime() >
|
||||
GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS
|
||||
) {
|
||||
// restart only if enough time was passed since last start to prevent restart loop
|
||||
gradidoNodeProcess.start()
|
||||
}
|
||||
},
|
||||
/*stdout: 'ignore',
|
||||
stderr: logger.isDebugEnabled() ? 'pipe' : 'ignore',*/
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
})
|
||||
}
|
||||
|
||||
public async restart() {
|
||||
if (this.proc) {
|
||||
await this.exit()
|
||||
this.exitCalled = false
|
||||
this.start()
|
||||
}
|
||||
}
|
||||
|
||||
public async exit(): Promise<void> {
|
||||
this.exitCalled = true
|
||||
if (this.proc) {
|
||||
this.proc.kill('SIGTERM')
|
||||
const timeout = setTimeout(() => {
|
||||
this.logger.warn(
|
||||
`GradidoNode couldn't exit graceful after ${GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS} milliseconds with SIGTERM, killing with SIGKILL`,
|
||||
)
|
||||
this.proc?.kill('SIGKILL')
|
||||
}, GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS)
|
||||
try {
|
||||
await this.proc.exited
|
||||
} catch (error) {
|
||||
this.logger.error(`GradidoNodeProcess exit error: ${error}`)
|
||||
} finally {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
84
dlt-connector/src/client/GradidoNode/communities.ts
Normal file
84
dlt-connector/src/client/GradidoNode/communities.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { Mutex } from 'async-mutex'
|
||||
import { getLogger } from 'log4js'
|
||||
import { CONFIG } from '../../config'
|
||||
import { GRADIDO_NODE_HOME_FOLDER_NAME, LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||
import { HieroId } from '../../schemas/typeGuard.schema'
|
||||
import { checkFileExist, checkPathExist } from '../../utils/filesystem'
|
||||
import { BackendClient } from '../backend/BackendClient'
|
||||
import { GradidoNodeProcess } from './GradidoNodeProcess'
|
||||
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.GradidoNode.communities`)
|
||||
const ensureCommunitiesAvailableMutex: Mutex = new Mutex()
|
||||
|
||||
// prototype, later add api call to gradido dlt node server for adding/updating communities
|
||||
type CommunityForDltNodeServer = {
|
||||
communityId: string
|
||||
hieroTopicId: string
|
||||
alias: string
|
||||
folder: string
|
||||
}
|
||||
|
||||
export async function ensureCommunitiesAvailable(communityTopicIds: HieroId[]): Promise<void> {
|
||||
const release = await ensureCommunitiesAvailableMutex.acquire()
|
||||
try {
|
||||
const homeFolder = path.join(
|
||||
CONFIG.DLT_GRADIDO_NODE_SERVER_HOME_FOLDER,
|
||||
GRADIDO_NODE_HOME_FOLDER_NAME,
|
||||
)
|
||||
if (!checkCommunityAvailable(communityTopicIds, homeFolder)) {
|
||||
await exportCommunities(homeFolder, BackendClient.getInstance())
|
||||
return GradidoNodeProcess.getInstance().restart()
|
||||
}
|
||||
} finally {
|
||||
release()
|
||||
}
|
||||
}
|
||||
|
||||
export async function exportCommunities(homeFolder: string, client: BackendClient): Promise<void> {
|
||||
const communities = await client.getReachableCommunities()
|
||||
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||
checkPathExist(path.dirname(communitiesPath), true)
|
||||
// make sure communityName is unique
|
||||
const communityName = new Set<string>()
|
||||
const communitiesForDltNodeServer: CommunityForDltNodeServer[] = []
|
||||
for (const com of communities) {
|
||||
if (!com.uuid || !com.hieroTopicId) {
|
||||
continue
|
||||
}
|
||||
// use name as alias if not empty and unique, otherwise use uuid
|
||||
let alias = com.name
|
||||
if (!alias || communityName.has(alias)) {
|
||||
alias = com.uuid
|
||||
}
|
||||
communityName.add(alias)
|
||||
communitiesForDltNodeServer.push({
|
||||
communityId: com.uuid,
|
||||
hieroTopicId: com.hieroTopicId,
|
||||
alias,
|
||||
// use only alpha-numeric chars for folder name
|
||||
folder: alias.replace(/[^a-zA-Z0-9]/g, '_'),
|
||||
})
|
||||
}
|
||||
fs.writeFileSync(communitiesPath, JSON.stringify(communitiesForDltNodeServer, null, 2))
|
||||
logger.info(`exported ${communitiesForDltNodeServer.length} communities to ${communitiesPath}`)
|
||||
}
|
||||
|
||||
export function checkCommunityAvailable(communityTopicIds: HieroId[], homeFolder: string): boolean {
|
||||
const communitiesPath = path.join(homeFolder, 'communities.json')
|
||||
if (!checkFileExist(communitiesPath)) {
|
||||
return false
|
||||
}
|
||||
const communities = JSON.parse(fs.readFileSync(communitiesPath, 'utf-8'))
|
||||
let foundCount = 0
|
||||
for (const community of communities) {
|
||||
if (communityTopicIds.includes(community.hieroTopicId)) {
|
||||
foundCount++
|
||||
if (foundCount >= communityTopicIds.length) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
60
dlt-connector/src/client/GradidoNode/input.schema.test.ts
Normal file
60
dlt-connector/src/client/GradidoNode/input.schema.test.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { beforeAll, describe, expect, it } from 'bun:test'
|
||||
import * as v from 'valibot'
|
||||
import {
|
||||
HieroId,
|
||||
HieroTransactionIdString,
|
||||
hieroIdSchema,
|
||||
hieroTransactionIdStringSchema,
|
||||
} from '../../schemas/typeGuard.schema'
|
||||
import { transactionIdentifierSchema } from './input.schema'
|
||||
|
||||
let topic: HieroId
|
||||
const topicString = '0.0.261'
|
||||
let hieroTransactionId: HieroTransactionIdString
|
||||
beforeAll(() => {
|
||||
topic = v.parse(hieroIdSchema, topicString)
|
||||
hieroTransactionId = v.parse(hieroTransactionIdStringSchema, '0.0.261-1755348116-1281621')
|
||||
})
|
||||
|
||||
describe('transactionIdentifierSchema ', () => {
|
||||
it('valid, transaction identified by transactionNr and topic', () => {
|
||||
expect(
|
||||
v.parse(transactionIdentifierSchema, {
|
||||
transactionId: 1,
|
||||
topic: topicString,
|
||||
}),
|
||||
).toEqual({
|
||||
transactionId: 1,
|
||||
hieroTransactionId: undefined,
|
||||
topic,
|
||||
})
|
||||
})
|
||||
it('valid, transaction identified by hieroTransactionId and topic', () => {
|
||||
expect(
|
||||
v.parse(transactionIdentifierSchema, {
|
||||
hieroTransactionId: '0.0.261-1755348116-1281621',
|
||||
topic: topicString,
|
||||
}),
|
||||
).toEqual({
|
||||
hieroTransactionId,
|
||||
topic,
|
||||
})
|
||||
})
|
||||
it('invalid, missing topic', () => {
|
||||
expect(() =>
|
||||
v.parse(transactionIdentifierSchema, {
|
||||
transactionId: 1,
|
||||
hieroTransactionId: '0.0.261-1755348116-1281621',
|
||||
}),
|
||||
).toThrowError(new Error('Invalid key: Expected "topic" but received undefined'))
|
||||
})
|
||||
it('invalid, transactionNr and iotaMessageId set', () => {
|
||||
expect(() =>
|
||||
v.parse(transactionIdentifierSchema, {
|
||||
transactionId: 1,
|
||||
hieroTransactionId: '0.0.261-1755348116-1281621',
|
||||
topic,
|
||||
}),
|
||||
).toThrowError(new Error('expect transactionNr or hieroTransactionId not both'))
|
||||
})
|
||||
})
|
||||
35
dlt-connector/src/client/GradidoNode/input.schema.ts
Normal file
35
dlt-connector/src/client/GradidoNode/input.schema.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import * as v from 'valibot'
|
||||
import { hieroIdSchema, hieroTransactionIdStringSchema } from '../../schemas/typeGuard.schema'
|
||||
|
||||
export const transactionsRangeSchema = v.object({
|
||||
// default value is 1, from first transactions
|
||||
fromTransactionId: v.nullish(v.pipe(v.number(), v.minValue(1, 'expect number >= 1')), 1),
|
||||
// default value is 100, max 100 transactions
|
||||
maxResultCount: v.nullish(v.pipe(v.number(), v.minValue(1, 'expect number >= 1')), 100),
|
||||
topic: hieroIdSchema,
|
||||
})
|
||||
|
||||
export type TransactionsRangeInput = v.InferInput<typeof transactionsRangeSchema>
|
||||
|
||||
// allow TransactionIdentifier to only contain either transactionNr or iotaMessageId
|
||||
export const transactionIdentifierSchema = v.pipe(
|
||||
v.object({
|
||||
transactionId: v.nullish(
|
||||
v.pipe(v.number('expect number type'), v.minValue(1, 'expect number >= 1')),
|
||||
undefined,
|
||||
),
|
||||
hieroTransactionId: v.nullish(hieroTransactionIdStringSchema, undefined),
|
||||
topic: hieroIdSchema,
|
||||
}),
|
||||
v.custom((value: any) => {
|
||||
const setFieldsCount =
|
||||
Number(value.transactionId !== undefined) + Number(value.hieroTransactionId !== undefined)
|
||||
if (setFieldsCount !== 1) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, 'expect transactionNr or hieroTransactionId not both'),
|
||||
)
|
||||
|
||||
export type TransactionIdentifierInput = v.InferInput<typeof transactionIdentifierSchema>
|
||||
export type TransactionIdentifier = v.InferOutput<typeof transactionIdentifierSchema>
|
||||
@ -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',
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -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'
|
||||
}
|
||||
```
|
||||
*/
|
||||
123
dlt-connector/src/client/backend/BackendClient.ts
Normal file
123
dlt-connector/src/client/backend/BackendClient.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import { SignJWT } from 'jose'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import * as v from 'valibot'
|
||||
import { CONFIG } from '../../config'
|
||||
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||
import { HieroId, Uuidv4 } from '../../schemas/typeGuard.schema'
|
||||
import {
|
||||
getReachableCommunities,
|
||||
homeCommunityGraphqlQuery,
|
||||
setHomeCommunityTopicId,
|
||||
} from './graphql'
|
||||
import { type Community, communitySchema } from './output.schema'
|
||||
|
||||
// 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.
|
||||
*/
|
||||
export class BackendClient {
|
||||
private static instance: BackendClient
|
||||
client: GraphQLClient
|
||||
logger: Logger
|
||||
urlValue: string
|
||||
|
||||
/**
|
||||
* The Singleton's constructor should always be private to prevent direct
|
||||
* construction calls with the `new` operator.
|
||||
*/
|
||||
private constructor() {
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.BackendClient`)
|
||||
this.urlValue = `http://localhost:${CONFIG.PORT}`
|
||||
this.logger.addContext('url', this.urlValue)
|
||||
|
||||
this.client = new GraphQLClient(this.urlValue, {
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
method: 'GET',
|
||||
jsonSerializer: {
|
||||
parse: JSON.parse,
|
||||
stringify: JSON.stringify,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public get url(): string {
|
||||
return this.urlValue
|
||||
}
|
||||
|
||||
/**
|
||||
* The static method that controls the access to the singleton instance.
|
||||
*
|
||||
* This implementation let you subclass the Singleton class while keeping
|
||||
* just one instance of each subclass around.
|
||||
*/
|
||||
public static getInstance(): BackendClient {
|
||||
if (!BackendClient.instance) {
|
||||
BackendClient.instance = new BackendClient()
|
||||
}
|
||||
return BackendClient.instance
|
||||
}
|
||||
|
||||
public async getHomeCommunityDraft(): Promise<Community> {
|
||||
this.logger.info('check home community on backend')
|
||||
const { data, errors } = await this.client.rawRequest<{ homeCommunity: Community }>(
|
||||
homeCommunityGraphqlQuery,
|
||||
{},
|
||||
await this.getRequestHeader(),
|
||||
)
|
||||
if (errors) {
|
||||
throw errors[0]
|
||||
}
|
||||
return v.parse(communitySchema, data.homeCommunity)
|
||||
}
|
||||
|
||||
public async setHomeCommunityTopicId(uuid: Uuidv4, hieroTopicId: HieroId): Promise<Community> {
|
||||
this.logger.info('update home community on backend')
|
||||
const { data, errors } = await this.client.rawRequest<{ updateHomeCommunity: Community }>(
|
||||
setHomeCommunityTopicId,
|
||||
{ uuid, hieroTopicId },
|
||||
await this.getRequestHeader(),
|
||||
)
|
||||
if (errors) {
|
||||
throw errors[0]
|
||||
}
|
||||
return v.parse(communitySchema, data.updateHomeCommunity)
|
||||
}
|
||||
|
||||
public async getReachableCommunities(): Promise<Community[]> {
|
||||
this.logger.info('get reachable communities on backend')
|
||||
const { data, errors } = await this.client.rawRequest<{ reachableCommunities: Community[] }>(
|
||||
getReachableCommunities,
|
||||
{},
|
||||
await this.getRequestHeader(),
|
||||
)
|
||||
if (errors) {
|
||||
throw errors[0]
|
||||
}
|
||||
return v.parse(v.array(communitySchema), data.reachableCommunities)
|
||||
}
|
||||
|
||||
private async getRequestHeader(): Promise<{
|
||||
authorization: string
|
||||
}> {
|
||||
return {
|
||||
authorization: 'Bearer ' + (await this.createJWTToken()),
|
||||
}
|
||||
}
|
||||
|
||||
private async createJWTToken(): Promise<string> {
|
||||
const secret = new TextEncoder().encode(CONFIG.JWT_SECRET)
|
||||
const token = await new SignJWT({ gradidoID: 'dlt-connector', 'urn:gradido:claim': true })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setIssuer('urn:gradido:issuer')
|
||||
.setAudience('urn:gradido:audience')
|
||||
.setExpirationTime('1m')
|
||||
.sign(secret)
|
||||
return token
|
||||
}
|
||||
}
|
||||
46
dlt-connector/src/client/backend/graphql.ts
Normal file
46
dlt-connector/src/client/backend/graphql.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { gql } from 'graphql-request'
|
||||
|
||||
/**
|
||||
* Schema Definitions for graphql requests
|
||||
*/
|
||||
|
||||
const communityFragment = gql`
|
||||
fragment Community_common on Community {
|
||||
uuid
|
||||
name
|
||||
hieroTopicId
|
||||
foreign
|
||||
creationDate
|
||||
}
|
||||
`
|
||||
|
||||
// graphql query for getting home community in tune with community schema
|
||||
export const homeCommunityGraphqlQuery = gql`
|
||||
query {
|
||||
homeCommunity {
|
||||
...Community_common
|
||||
}
|
||||
}
|
||||
${communityFragment}
|
||||
`
|
||||
|
||||
export const setHomeCommunityTopicId = gql`
|
||||
mutation ($uuid: String!, $hieroTopicId: String){
|
||||
updateHomeCommunity(uuid: $uuid, hieroTopicId: $hieroTopicId) {
|
||||
uuid
|
||||
name
|
||||
hieroTopicId
|
||||
foreign
|
||||
creationDate
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const getReachableCommunities = gql`
|
||||
query {
|
||||
reachableCommunities {
|
||||
...Community_common
|
||||
}
|
||||
}
|
||||
${communityFragment}
|
||||
`
|
||||
25
dlt-connector/src/client/backend/output.schema.test.ts
Normal file
25
dlt-connector/src/client/backend/output.schema.test.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// only for IDE, bun don't need this to work
|
||||
import { describe, expect, it } from 'bun:test'
|
||||
import * as v from 'valibot'
|
||||
import { hieroIdSchema, uuidv4Schema } from '../../schemas/typeGuard.schema'
|
||||
import { communitySchema } from './output.schema'
|
||||
|
||||
describe('community.schema', () => {
|
||||
it('community', () => {
|
||||
expect(
|
||||
v.parse(communitySchema, {
|
||||
uuid: '4f28e081-5c39-4dde-b6a4-3bde71de8d65',
|
||||
hieroTopicId: '0.0.4',
|
||||
foreign: false,
|
||||
name: 'Test',
|
||||
creationDate: '2021-01-01',
|
||||
}),
|
||||
).toEqual({
|
||||
hieroTopicId: v.parse(hieroIdSchema, '0.0.4'),
|
||||
uuid: v.parse(uuidv4Schema, '4f28e081-5c39-4dde-b6a4-3bde71de8d65'),
|
||||
foreign: false,
|
||||
name: 'Test',
|
||||
creationDate: new Date('2021-01-01'),
|
||||
})
|
||||
})
|
||||
})
|
||||
14
dlt-connector/src/client/backend/output.schema.ts
Normal file
14
dlt-connector/src/client/backend/output.schema.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import * as v from 'valibot'
|
||||
import { dateSchema } from '../../schemas/typeConverter.schema'
|
||||
import { hieroIdSchema, uuidv4Schema } from '../../schemas/typeGuard.schema'
|
||||
|
||||
export const communitySchema = v.object({
|
||||
uuid: uuidv4Schema,
|
||||
name: v.string('expect string type'),
|
||||
hieroTopicId: v.nullish(hieroIdSchema),
|
||||
foreign: v.boolean('expect boolean type'),
|
||||
creationDate: dateSchema,
|
||||
})
|
||||
|
||||
export type CommunityInput = v.InferInput<typeof communitySchema>
|
||||
export type Community = v.InferOutput<typeof communitySchema>
|
||||
195
dlt-connector/src/client/hiero/HieroClient.ts
Normal file
195
dlt-connector/src/client/hiero/HieroClient.ts
Normal file
@ -0,0 +1,195 @@
|
||||
import {
|
||||
AccountBalance,
|
||||
AccountBalanceQuery,
|
||||
AddressBookQuery,
|
||||
Client,
|
||||
FileId,
|
||||
LocalProvider,
|
||||
NodeAddressBook,
|
||||
PrivateKey,
|
||||
TopicCreateTransaction,
|
||||
TopicId,
|
||||
TopicInfoQuery,
|
||||
TopicMessageSubmitTransaction,
|
||||
TopicUpdateTransaction,
|
||||
TransactionId,
|
||||
Wallet,
|
||||
} from '@hashgraph/sdk'
|
||||
import { GradidoTransaction } from 'gradido-blockchain-js'
|
||||
import { getLogger, Logger } from 'log4js'
|
||||
import * as v from 'valibot'
|
||||
import { CONFIG } from '../../config'
|
||||
import { LOG4JS_BASE_CATEGORY } from '../../config/const'
|
||||
import { HieroId, hieroIdSchema } from '../../schemas/typeGuard.schema'
|
||||
import { type TopicInfoOutput, topicInfoSchema } from './output.schema'
|
||||
|
||||
// https://docs.hedera.com/hedera/sdks-and-apis/hedera-api/consensus/consensusupdatetopic
|
||||
export const MIN_AUTORENEW_PERIOD = 6999999 //seconds
|
||||
export const MAX_AUTORENEW_PERIOD = 8000001 // seconds
|
||||
|
||||
export class HieroClient {
|
||||
private static instance: HieroClient
|
||||
wallet: Wallet
|
||||
client: Client
|
||||
logger: Logger
|
||||
// transaction counter for logging
|
||||
transactionInternNr: number = 0
|
||||
pendingPromises: Promise<void>[] = []
|
||||
|
||||
private constructor() {
|
||||
this.logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
||||
this.client = Client.forName(CONFIG.HIERO_HEDERA_NETWORK)
|
||||
const provider = LocalProvider.fromClient(this.client)
|
||||
let operatorKey: PrivateKey
|
||||
if (CONFIG.HIERO_OPERATOR_KEY.length === 64) {
|
||||
operatorKey = PrivateKey.fromStringED25519(CONFIG.HIERO_OPERATOR_KEY)
|
||||
} else {
|
||||
operatorKey = PrivateKey.fromStringECDSA(CONFIG.HIERO_OPERATOR_KEY)
|
||||
}
|
||||
this.wallet = new Wallet(CONFIG.HIERO_OPERATOR_ID, operatorKey, provider)
|
||||
this.client.setOperator(CONFIG.HIERO_OPERATOR_ID, operatorKey)
|
||||
}
|
||||
|
||||
public static getInstance(): HieroClient {
|
||||
if (!HieroClient.instance) {
|
||||
HieroClient.instance = new HieroClient()
|
||||
}
|
||||
|
||||
return HieroClient.instance
|
||||
}
|
||||
|
||||
public async waitForPendingPromises() {
|
||||
const startTime = new Date()
|
||||
this.logger.info(`waiting for ${this.pendingPromises.length} pending promises`)
|
||||
await Promise.all(this.pendingPromises)
|
||||
const endTime = new Date()
|
||||
this.logger.info(
|
||||
`all pending promises resolved, used time: ${endTime.getTime() - startTime.getTime()}ms`,
|
||||
)
|
||||
}
|
||||
|
||||
public async sendMessage(
|
||||
topicId: HieroId,
|
||||
transaction: GradidoTransaction,
|
||||
): Promise<TransactionId | null> {
|
||||
const startTime = new Date()
|
||||
this.transactionInternNr++
|
||||
const logger = getLogger(`${LOG4JS_BASE_CATEGORY}.client.HieroClient`)
|
||||
logger.addContext('trNr', this.transactionInternNr)
|
||||
logger.addContext('topicId', topicId.toString())
|
||||
const serializedTransaction = transaction.getSerializedTransaction()
|
||||
if (!serializedTransaction) {
|
||||
throw new Error('cannot serialize transaction')
|
||||
}
|
||||
// send one message
|
||||
const hieroTransaction = await new TopicMessageSubmitTransaction({
|
||||
topicId,
|
||||
message: serializedTransaction.data(),
|
||||
}).freezeWithSigner(this.wallet)
|
||||
// sign and execute transaction needs some time, so let it run in background
|
||||
const pendingPromiseIndex = this.pendingPromises.push(
|
||||
hieroTransaction
|
||||
.signWithSigner(this.wallet)
|
||||
.then(async (signedHieroTransaction) => {
|
||||
const sendResponse = await signedHieroTransaction.executeWithSigner(this.wallet)
|
||||
logger.info(
|
||||
`message sent to topic ${topicId}, transaction id: ${sendResponse.transactionId.toString()}`,
|
||||
)
|
||||
if (logger.isInfoEnabled()) {
|
||||
// only for logging
|
||||
sendResponse.getReceiptWithSigner(this.wallet).then((receipt) => {
|
||||
logger.info(`message send status: ${receipt.status.toString()}`)
|
||||
})
|
||||
// only for logging
|
||||
sendResponse.getRecordWithSigner(this.wallet).then((record) => {
|
||||
logger.info(`message sent, cost: ${record.transactionFee.toString()}`)
|
||||
const localEndTime = new Date()
|
||||
logger.info(
|
||||
`HieroClient.sendMessage used time (full process): ${localEndTime.getTime() - startTime.getTime()}ms`,
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.error(e)
|
||||
})
|
||||
.finally(() => {
|
||||
this.pendingPromises.splice(pendingPromiseIndex, 1)
|
||||
}),
|
||||
)
|
||||
const endTime = new Date()
|
||||
logger.info(`HieroClient.sendMessage used time: ${endTime.getTime() - startTime.getTime()}ms`)
|
||||
return hieroTransaction.transactionId
|
||||
}
|
||||
|
||||
public async getBalance(): Promise<AccountBalance> {
|
||||
const balance = await new AccountBalanceQuery()
|
||||
.setAccountId(this.wallet.getAccountId())
|
||||
.executeWithSigner(this.wallet)
|
||||
return balance
|
||||
}
|
||||
|
||||
public async getTopicInfo(topicId: HieroId): Promise<TopicInfoOutput> {
|
||||
this.logger.addContext('topicId', topicId.toString())
|
||||
const info = await new TopicInfoQuery()
|
||||
.setTopicId(TopicId.fromString(topicId))
|
||||
.execute(this.client)
|
||||
this.logger.info(`topic is valid until ${info.expirationTime?.toDate()?.toLocaleString()}`)
|
||||
if (info.topicMemo) {
|
||||
this.logger.info(`topic memo: ${info.topicMemo}`)
|
||||
}
|
||||
this.logger.debug(`topic sequence number: ${info.sequenceNumber.toNumber()}`)
|
||||
// this.logger.debug(JSON.stringify(info, null, 2))
|
||||
return v.parse(topicInfoSchema, {
|
||||
topicId: topicId.toString(),
|
||||
sequenceNumber: info.sequenceNumber.toNumber(),
|
||||
expirationTime: info.expirationTime?.toDate(),
|
||||
autoRenewPeriod: info.autoRenewPeriod?.seconds.toNumber(),
|
||||
autoRenewAccountId: info.autoRenewAccountId?.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
public async createTopic(topicMemo?: string): Promise<HieroId> {
|
||||
let transaction = new TopicCreateTransaction({
|
||||
topicMemo,
|
||||
adminKey: undefined,
|
||||
submitKey: undefined,
|
||||
autoRenewPeriod: undefined,
|
||||
autoRenewAccountId: undefined,
|
||||
})
|
||||
|
||||
transaction = await transaction.freezeWithSigner(this.wallet)
|
||||
transaction = await transaction.signWithSigner(this.wallet)
|
||||
const createResponse = await transaction.executeWithSigner(this.wallet)
|
||||
const createReceipt = await createResponse.getReceiptWithSigner(this.wallet)
|
||||
this.logger.debug(createReceipt.toString())
|
||||
this.logger.addContext('topicId', createReceipt.topicId?.toString())
|
||||
const record = await createResponse.getRecordWithSigner(this.wallet)
|
||||
this.logger.info(`topic created, cost: ${record.transactionFee.toString()}`)
|
||||
return v.parse(hieroIdSchema, createReceipt.topicId?.toString())
|
||||
}
|
||||
|
||||
public async downloadAddressBook(): Promise<NodeAddressBook> {
|
||||
const query = new AddressBookQuery().setFileId(FileId.ADDRESS_BOOK)
|
||||
try {
|
||||
return await query.execute(this.client)
|
||||
} catch (e) {
|
||||
this.logger.error(e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public async updateTopic(topicId: HieroId): Promise<void> {
|
||||
this.logger.addContext('topicId', topicId.toString())
|
||||
let transaction = new TopicUpdateTransaction()
|
||||
transaction.setExpirationTime(new Date(new Date().getTime() + MIN_AUTORENEW_PERIOD * 1000))
|
||||
transaction.setTopicId(TopicId.fromString(topicId))
|
||||
transaction = await transaction.freezeWithSigner(this.wallet)
|
||||
transaction = await transaction.signWithSigner(this.wallet)
|
||||
const updateResponse = await transaction.executeWithSigner(this.wallet)
|
||||
const updateReceipt = await updateResponse.getReceiptWithSigner(this.wallet)
|
||||
this.logger.debug(updateReceipt.toString())
|
||||
const record = await updateResponse.getRecordWithSigner(this.wallet)
|
||||
this.logger.info(`topic updated, cost: ${record.transactionFee.toString()}`)
|
||||
}
|
||||
}
|
||||
21
dlt-connector/src/client/hiero/output.schema.ts
Normal file
21
dlt-connector/src/client/hiero/output.schema.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import * as v from 'valibot'
|
||||
import { dateSchema } from '../../schemas/typeConverter.schema'
|
||||
import { hieroIdSchema } from '../../schemas/typeGuard.schema'
|
||||
|
||||
// schema definitions for exporting data from hiero request as json back to caller
|
||||
/*export const dateStringSchema = v.pipe(
|
||||
v.enum([v.string(), v.date()],
|
||||
v.transform(in: string | Date)
|
||||
|
||||
)*/
|
||||
export const positiveNumberSchema = v.pipe(v.number(), v.minValue(0))
|
||||
|
||||
export const topicInfoSchema = v.object({
|
||||
topicId: hieroIdSchema,
|
||||
sequenceNumber: positiveNumberSchema,
|
||||
expirationTime: dateSchema,
|
||||
autoRenewPeriod: v.optional(positiveNumberSchema, 0),
|
||||
autoRenewAccountId: v.optional(hieroIdSchema, '0.0.0'),
|
||||
})
|
||||
|
||||
export type TopicInfoOutput = v.InferOutput<typeof topicInfoSchema>
|
||||
21
dlt-connector/src/config/const.ts
Normal file
21
dlt-connector/src/config/const.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import path from 'node:path'
|
||||
|
||||
export const LOG4JS_BASE_CATEGORY = 'dlt'
|
||||
// 7 days
|
||||
export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_UPDATE = 1000 * 60 * 60 * 24 * 7
|
||||
// 10 minutes
|
||||
export const MIN_TOPIC_EXPIRE_MILLISECONDS_FOR_SEND_MESSAGE = 1000 * 60 * 10
|
||||
|
||||
export const GRADIDO_NODE_RUNTIME_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'gradido_node',
|
||||
'bin',
|
||||
'GradidoNode',
|
||||
)
|
||||
// if last start was less than this time, do not restart
|
||||
export const GRADIDO_NODE_MIN_RUNTIME_BEFORE_RESTART_MILLISECONDS = 1000 * 30
|
||||
export const GRADIDO_NODE_KILL_TIMEOUT_MILLISECONDS = 10000
|
||||
// currently hard coded in gradido node, update in future
|
||||
export const GRADIDO_NODE_HOME_FOLDER_NAME = '.gradido'
|
||||
@ -1,65 +1,25 @@
|
||||
/* eslint-disable n/no-process-env */
|
||||
import dotenv from 'dotenv'
|
||||
import * as v from 'valibot'
|
||||
import { configSchema } from './schema'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const constants = {
|
||||
LOG4JS_CONFIG: 'log4js-config.json',
|
||||
DB_VERSION: '0003-refactor_transaction_recipe',
|
||||
// default log level on production should be info
|
||||
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',
|
||||
CONFIG_VERSION: {
|
||||
DEFAULT: 'DEFAULT',
|
||||
EXPECTED: 'v6.2024-02-20',
|
||||
CURRENT: '',
|
||||
},
|
||||
type ConfigOutput = v.InferOutput<typeof configSchema>
|
||||
|
||||
let config: ConfigOutput
|
||||
try {
|
||||
config = v.parse(configSchema, process.env)
|
||||
} catch (error) {
|
||||
if (error instanceof v.ValiError) {
|
||||
// biome-ignore lint/suspicious/noConsole: need to parse config before initializing logger
|
||||
console.error(
|
||||
`${error.issues[0].path[0].key}: ${error.message} received: ${error.issues[0].received}`,
|
||||
)
|
||||
} else {
|
||||
// biome-ignore lint/suspicious/noConsole: need to parse config before initializing logger
|
||||
console.error(error)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const server = {
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
|
||||
JWT_SECRET: process.env.JWT_SECRET ?? 'secret123',
|
||||
}
|
||||
|
||||
const database = {
|
||||
DB_HOST: process.env.DB_HOST ?? 'localhost',
|
||||
DB_PORT: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306,
|
||||
DB_USER: process.env.DB_USER ?? 'root',
|
||||
DB_PASSWORD: process.env.DB_PASSWORD ?? '',
|
||||
DB_DATABASE: process.env.DB_DATABASE ?? 'gradido_dlt',
|
||||
DB_DATABASE_TEST: process.env.DB_DATABASE_TEST ?? 'gradido_dlt_test',
|
||||
TYPEORM_LOGGING_RELATIVE_PATH: process.env.TYPEORM_LOGGING_RELATIVE_PATH ?? 'typeorm.backend.log',
|
||||
}
|
||||
|
||||
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?.substring(0, 32) ?? null,
|
||||
}
|
||||
|
||||
const dltConnector = {
|
||||
DLT_CONNECTOR_PORT: process.env.DLT_CONNECTOR_PORT ?? 6010,
|
||||
}
|
||||
|
||||
const backendServer = {
|
||||
BACKEND_SERVER_URL: process.env.BACKEND_SERVER_URL ?? 'http://backend:4000',
|
||||
}
|
||||
|
||||
// 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}"`,
|
||||
)
|
||||
}
|
||||
|
||||
export const CONFIG = {
|
||||
...constants,
|
||||
...server,
|
||||
...database,
|
||||
...iota,
|
||||
...dltConnector,
|
||||
...backendServer,
|
||||
}
|
||||
export const CONFIG = config
|
||||
|
||||
99
dlt-connector/src/config/schema.ts
Normal file
99
dlt-connector/src/config/schema.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import path from 'node:path'
|
||||
import { MemoryBlock } from 'gradido-blockchain-js'
|
||||
import * as v from 'valibot'
|
||||
|
||||
const hexSchema = v.pipe(v.string('expect string type'), v.hexadecimal('expect hexadecimal string'))
|
||||
|
||||
const hex16Schema = v.pipe(hexSchema, v.length(32, 'expect string length = 32'))
|
||||
|
||||
export const configSchema = v.object({
|
||||
LOG4JS_CONFIG: v.optional(
|
||||
v.string('The path to the log4js configuration file'),
|
||||
'./log4js-config.json',
|
||||
),
|
||||
LOG_LEVEL: v.optional(v.string('The log level'), 'info'),
|
||||
DLT_CONNECTOR_PORT: v.optional(
|
||||
v.pipe(
|
||||
v.string('A valid port on which the DLT connector is running'),
|
||||
v.transform<string, number>((input: string) => Number(input)),
|
||||
v.minValue(1),
|
||||
v.maxValue(65535),
|
||||
),
|
||||
'6010',
|
||||
),
|
||||
JWT_SECRET: v.optional(
|
||||
v.pipe(
|
||||
v.string('The JWT secret for connecting to the backend'),
|
||||
v.custom((input: unknown): boolean => {
|
||||
if (process.env.NODE_ENV === 'production' && input === 'secret123') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, "Shouldn't use default value in production"),
|
||||
),
|
||||
'secret123',
|
||||
),
|
||||
GRADIDO_BLOCKCHAIN_CRYPTO_APP_SECRET: hexSchema,
|
||||
GRADIDO_BLOCKCHAIN_SERVER_CRYPTO_KEY: hex16Schema,
|
||||
HOME_COMMUNITY_SEED: v.pipe(
|
||||
hexSchema,
|
||||
v.length(64, 'expect seed length minimum 64 characters (32 Bytes)'),
|
||||
v.transform<string, MemoryBlock>((input: string) => MemoryBlock.fromHex(input)),
|
||||
),
|
||||
HIERO_HEDERA_NETWORK: v.optional(
|
||||
v.union([v.literal('mainnet'), v.literal('testnet'), v.literal('previewnet')]),
|
||||
'testnet',
|
||||
),
|
||||
HIERO_OPERATOR_ID: v.pipe(
|
||||
v.string('The operator ID (Account id) for Hiero integration'),
|
||||
v.regex(/^[0-9]+\.[0-9]+\.[0-9]+$/),
|
||||
),
|
||||
HIERO_OPERATOR_KEY: v.pipe(
|
||||
v.string('The operator key (Private key) for Hiero integration'),
|
||||
v.hexadecimal(),
|
||||
v.minLength(64),
|
||||
v.maxLength(96),
|
||||
),
|
||||
CONNECT_TIMEOUT_MS: v.optional(
|
||||
v.pipe(v.number('The connection timeout in milliseconds'), v.minValue(200), v.maxValue(120000)),
|
||||
1000,
|
||||
),
|
||||
CONNECT_RETRY_COUNT: v.optional(
|
||||
v.pipe(v.number('The connection retry count'), v.minValue(1), v.maxValue(50)),
|
||||
15,
|
||||
),
|
||||
CONNECT_RETRY_DELAY_MS: v.optional(
|
||||
v.pipe(
|
||||
v.number('The connection retry delay in milliseconds'),
|
||||
v.minValue(100),
|
||||
v.maxValue(10000),
|
||||
),
|
||||
500,
|
||||
),
|
||||
DLT_NODE_SERVER_PORT: v.optional(
|
||||
v.pipe(
|
||||
v.string('A valid port on which the DLT node server is running'),
|
||||
v.transform<string, number>((input: string) => Number(input)),
|
||||
v.minValue(1),
|
||||
v.maxValue(65535),
|
||||
),
|
||||
'8340',
|
||||
),
|
||||
DLT_GRADIDO_NODE_SERVER_VERSION: v.optional(
|
||||
v.string('The version of the DLT node server'),
|
||||
'0.9.0',
|
||||
),
|
||||
DLT_GRADIDO_NODE_SERVER_HOME_FOLDER: v.optional(
|
||||
v.string('The home folder for the gradido dlt node server'),
|
||||
path.join(__dirname, '..', '..', 'gradido_node'),
|
||||
),
|
||||
PORT: v.optional(
|
||||
v.pipe(
|
||||
v.string('A valid port on which the backend server is running'),
|
||||
v.transform<string, number>((input: string) => Number(input)),
|
||||
v.minValue(1),
|
||||
v.maxValue(65535),
|
||||
),
|
||||
'4000',
|
||||
),
|
||||
})
|
||||
@ -1,60 +0,0 @@
|
||||
import { Account } from '@entity/Account'
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
import { KeyPair } from '@/data/KeyPair'
|
||||
import { AddressType } from '@/data/proto/3_3/enum/AddressType'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { hardenDerivationIndex } from '@/utils/derivationHelper'
|
||||
import { accountTypeToAddressType } from '@/utils/typeConverter'
|
||||
|
||||
const GMW_ACCOUNT_DERIVATION_INDEX = 1
|
||||
const AUF_ACCOUNT_DERIVATION_INDEX = 2
|
||||
|
||||
export class AccountFactory {
|
||||
public static createAccount(
|
||||
createdAt: Date,
|
||||
derivationIndex: number,
|
||||
type: AddressType,
|
||||
parentKeyPair: KeyPair,
|
||||
): Account {
|
||||
const account = Account.create()
|
||||
account.derivationIndex = derivationIndex
|
||||
account.derive2Pubkey = parentKeyPair.derive([derivationIndex]).publicKey
|
||||
account.type = type.valueOf()
|
||||
account.createdAt = createdAt
|
||||
account.balanceOnConfirmation = new Decimal(0)
|
||||
account.balanceOnCreation = new Decimal(0)
|
||||
account.balanceCreatedAt = createdAt
|
||||
return account
|
||||
}
|
||||
|
||||
public static createAccountFromUserAccountDraft(
|
||||
{ createdAt, accountType, user }: UserAccountDraft,
|
||||
parentKeyPair: KeyPair,
|
||||
): Account {
|
||||
return AccountFactory.createAccount(
|
||||
new Date(createdAt),
|
||||
user.accountNr ?? 1,
|
||||
accountTypeToAddressType(accountType),
|
||||
parentKeyPair,
|
||||
)
|
||||
}
|
||||
|
||||
public static createGmwAccount(keyPair: KeyPair, createdAt: Date): Account {
|
||||
return AccountFactory.createAccount(
|
||||
createdAt,
|
||||
hardenDerivationIndex(GMW_ACCOUNT_DERIVATION_INDEX),
|
||||
AddressType.COMMUNITY_GMW,
|
||||
keyPair,
|
||||
)
|
||||
}
|
||||
|
||||
public static createAufAccount(keyPair: KeyPair, createdAt: Date): Account {
|
||||
return AccountFactory.createAccount(
|
||||
createdAt,
|
||||
hardenDerivationIndex(AUF_ACCOUNT_DERIVATION_INDEX),
|
||||
AddressType.COMMUNITY_AUF,
|
||||
keyPair,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import { Account } from '@entity/Account'
|
||||
|
||||
import { LogError } from '@/server/LogError'
|
||||
|
||||
import { KeyPair } from './KeyPair'
|
||||
import { UserLogic } from './User.logic'
|
||||
|
||||
export class AccountLogic {
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
public constructor(private self: Account) {}
|
||||
|
||||
/**
|
||||
* calculate account key pair starting from community key pair => derive user key pair => derive account key pair
|
||||
* @param communityKeyPair
|
||||
*/
|
||||
public calculateKeyPair(communityKeyPair: KeyPair): KeyPair {
|
||||
if (!this.self.user) {
|
||||
throw new LogError('missing user')
|
||||
}
|
||||
const userLogic = new UserLogic(this.self.user)
|
||||
const accountKeyPair = userLogic
|
||||
.calculateKeyPair(communityKeyPair)
|
||||
.derive([this.self.derivationIndex])
|
||||
|
||||
if (
|
||||
this.self.derive2Pubkey &&
|
||||
this.self.derive2Pubkey.compare(accountKeyPair.publicKey) !== 0
|
||||
) {
|
||||
throw new LogError(
|
||||
'The freshly derived public key does not correspond to the stored public key',
|
||||
)
|
||||
}
|
||||
return accountKeyPair
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
import { Account } from '@entity/Account'
|
||||
import { User } from '@entity/User'
|
||||
import { In } from 'typeorm'
|
||||
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
export const AccountRepository = getDataSource()
|
||||
.getRepository(Account)
|
||||
.extend({
|
||||
findAccountsByPublicKeys(publicKeys: Buffer[]): Promise<Account[]> {
|
||||
return this.findBy({ derive2Pubkey: In(publicKeys) })
|
||||
},
|
||||
|
||||
async findAccountByPublicKey(publicKey: Buffer | undefined): Promise<Account | undefined> {
|
||||
if (!publicKey) return undefined
|
||||
return (await this.findOneBy({ derive2Pubkey: Buffer.from(publicKey) })) ?? undefined
|
||||
},
|
||||
|
||||
async findAccountByUserIdentifier({
|
||||
uuid,
|
||||
accountNr,
|
||||
}: UserIdentifier): Promise<Account | undefined> {
|
||||
const user = await User.findOne({
|
||||
where: { gradidoID: uuid, accounts: { derivationIndex: accountNr ?? 1 } },
|
||||
relations: { accounts: true },
|
||||
})
|
||||
if (user && user.accounts?.length === 1) {
|
||||
const account = user.accounts[0]
|
||||
account.user = user
|
||||
return account
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -1,197 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
import { Decimal } from 'decimal.js-light'
|
||||
|
||||
import { TestDB } from '@test/TestDB'
|
||||
|
||||
import { AccountType } from '@/graphql/enum/AccountType'
|
||||
import { UserAccountDraft } from '@/graphql/input/UserAccountDraft'
|
||||
import { UserIdentifier } from '@/graphql/input/UserIdentifier'
|
||||
|
||||
import { AccountFactory } from './Account.factory'
|
||||
import { AccountRepository } from './Account.repository'
|
||||
import { KeyPair } from './KeyPair'
|
||||
import { Mnemonic } from './Mnemonic'
|
||||
import { AddressType } from './proto/3_3/enum/AddressType'
|
||||
import { UserFactory } from './User.factory'
|
||||
import { UserLogic } from './User.logic'
|
||||
|
||||
const con = TestDB.instance
|
||||
|
||||
jest.mock('@typeorm/DataSource', () => ({
|
||||
getDataSource: jest.fn(() => TestDB.instance.dbConnect),
|
||||
}))
|
||||
|
||||
describe('data/Account test factory and repository', () => {
|
||||
const now = new Date()
|
||||
const keyPair1 = new KeyPair(new Mnemonic('62ef251edc2416f162cd24ab1711982b'))
|
||||
const keyPair2 = new KeyPair(new Mnemonic('000a0000000002000000000003000070'))
|
||||
const keyPair3 = new KeyPair(new Mnemonic('00ba541a1000020000000000300bda70'))
|
||||
const userGradidoID = '6be949ab-8198-4acf-ba63-740089081d61'
|
||||
|
||||
describe('test factory methods', () => {
|
||||
beforeAll(async () => {
|
||||
await con.setupTestDB()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await con.teardownTestDB()
|
||||
})
|
||||
|
||||
it('test createAccount', () => {
|
||||
const account = AccountFactory.createAccount(now, 1, AddressType.COMMUNITY_HUMAN, keyPair1)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 1,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_HUMAN,
|
||||
createdAt: now,
|
||||
balanceCreatedAt: now,
|
||||
balanceOnConfirmation: new Decimal(0),
|
||||
balanceOnCreation: new Decimal(0),
|
||||
})
|
||||
})
|
||||
|
||||
it('test createAccountFromUserAccountDraft', () => {
|
||||
const userAccountDraft = new UserAccountDraft()
|
||||
userAccountDraft.createdAt = now.toISOString()
|
||||
userAccountDraft.accountType = AccountType.COMMUNITY_HUMAN
|
||||
userAccountDraft.user = new UserIdentifier()
|
||||
userAccountDraft.user.accountNr = 1
|
||||
const account = AccountFactory.createAccountFromUserAccountDraft(userAccountDraft, keyPair1)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 1,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'cb88043ef4833afc01d6ed9b34e1aa48e79dce5ff97c07090c6600ec05f6d994',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_HUMAN,
|
||||
createdAt: now,
|
||||
balanceCreatedAt: now,
|
||||
balanceOnConfirmation: new Decimal(0),
|
||||
balanceOnCreation: new Decimal(0),
|
||||
})
|
||||
})
|
||||
|
||||
it('test createGmwAccount', () => {
|
||||
const account = AccountFactory.createGmwAccount(keyPair1, now)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 2147483649,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'05f0060357bb73bd290283870fc47a10b3764f02ca26938479ed853f46145366',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_GMW,
|
||||
createdAt: now,
|
||||
balanceCreatedAt: now,
|
||||
balanceOnConfirmation: new Decimal(0),
|
||||
balanceOnCreation: new Decimal(0),
|
||||
})
|
||||
})
|
||||
|
||||
it('test createAufAccount', () => {
|
||||
const account = AccountFactory.createAufAccount(keyPair1, now)
|
||||
expect(account).toMatchObject({
|
||||
derivationIndex: 2147483650,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_AUF,
|
||||
createdAt: now,
|
||||
balanceCreatedAt: now,
|
||||
balanceOnConfirmation: new Decimal(0),
|
||||
balanceOnCreation: new Decimal(0),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('test repository functions', () => {
|
||||
beforeAll(async () => {
|
||||
await con.setupTestDB()
|
||||
await Promise.all([
|
||||
AccountFactory.createAufAccount(keyPair1, now).save(),
|
||||
AccountFactory.createGmwAccount(keyPair1, now).save(),
|
||||
AccountFactory.createAufAccount(keyPair2, now).save(),
|
||||
AccountFactory.createGmwAccount(keyPair2, now).save(),
|
||||
AccountFactory.createAufAccount(keyPair3, now).save(),
|
||||
AccountFactory.createGmwAccount(keyPair3, now).save(),
|
||||
])
|
||||
const userAccountDraft = new UserAccountDraft()
|
||||
userAccountDraft.accountType = AccountType.COMMUNITY_HUMAN
|
||||
userAccountDraft.createdAt = now.toString()
|
||||
userAccountDraft.user = new UserIdentifier()
|
||||
userAccountDraft.user.accountNr = 1
|
||||
userAccountDraft.user.uuid = userGradidoID
|
||||
const user = UserFactory.create(userAccountDraft, keyPair1)
|
||||
const userLogic = new UserLogic(user)
|
||||
const account = AccountFactory.createAccountFromUserAccountDraft(
|
||||
userAccountDraft,
|
||||
userLogic.calculateKeyPair(keyPair1),
|
||||
)
|
||||
account.user = user
|
||||
// user is set to cascade: ['insert'] will be saved together with account
|
||||
await account.save()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await con.teardownTestDB()
|
||||
})
|
||||
it('test findAccountsByPublicKeys', async () => {
|
||||
const accounts = await AccountRepository.findAccountsByPublicKeys([
|
||||
Buffer.from('6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', 'hex'),
|
||||
Buffer.from('0fa996b73b624592fe326b8500cb1e3f10026112b374d84c87d097f4d489c019', 'hex'),
|
||||
Buffer.from('0ffa996b73b624592f26b850b0cb1e3f1026112b374d84c87d017f4d489c0197', 'hex'), // invalid
|
||||
])
|
||||
expect(accounts).toHaveLength(2)
|
||||
expect(accounts).toMatchObject(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
derivationIndex: 2147483649,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'0fa996b73b624592fe326b8500cb1e3f10026112b374d84c87d097f4d489c019',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_GMW,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
derivationIndex: 2147483650,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_AUF,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('test findAccountByPublicKey', async () => {
|
||||
expect(
|
||||
await AccountRepository.findAccountByPublicKey(
|
||||
Buffer.from('6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59', 'hex'),
|
||||
),
|
||||
).toMatchObject({
|
||||
derivationIndex: 2147483650,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'6c749f8693a4a58c948e5ae54df11e2db33d2f98673b56e0cf19c0132614ab59',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_AUF,
|
||||
})
|
||||
})
|
||||
|
||||
it('test findAccountByUserIdentifier', async () => {
|
||||
const userIdentifier = new UserIdentifier()
|
||||
userIdentifier.accountNr = 1
|
||||
userIdentifier.uuid = userGradidoID
|
||||
expect(await AccountRepository.findAccountByUserIdentifier(userIdentifier)).toMatchObject({
|
||||
derivationIndex: 1,
|
||||
derive2Pubkey: Buffer.from(
|
||||
'2099c004a26e5387c9fbbc9bb0f552a9642d3fd7c710ae5802b775d24ff36f93',
|
||||
'hex',
|
||||
),
|
||||
type: AddressType.COMMUNITY_HUMAN,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,5 +1,3 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
/**
|
||||
* enum for graphql
|
||||
* describe input account type in UserAccountDraft
|
||||
@ -12,10 +10,6 @@ export enum AccountType {
|
||||
COMMUNITY_AUF = 'COMMUNITY_AUF', // community compensation and environment founds account
|
||||
COMMUNITY_PROJECT = 'COMMUNITY_PROJECT', // no creations allowed
|
||||
SUBACCOUNT = 'SUBACCOUNT', // no creations allowed
|
||||
CRYPTO_ACCOUNT = 'CRYPTO_ACCOUNT', // user control his keys, no creations
|
||||
CRYPTO_ACCOUNT = 'CRYPTO_ACCOUNT', // user control his keys, no creations,
|
||||
DEFERRED_TRANSFER = 'DEFERRED_TRANSFER', // no creations allowed
|
||||
}
|
||||
|
||||
registerEnumType(AccountType, {
|
||||
name: 'AccountType', // this one is mandatory
|
||||
description: 'Type of account', // this one is optional
|
||||
})
|
||||
21
dlt-connector/src/data/AddressType.enum.ts
Normal file
21
dlt-connector/src/data/AddressType.enum.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {
|
||||
AddressType_COMMUNITY_AUF,
|
||||
AddressType_COMMUNITY_GMW,
|
||||
AddressType_COMMUNITY_HUMAN,
|
||||
AddressType_COMMUNITY_PROJECT,
|
||||
AddressType_CRYPTO_ACCOUNT,
|
||||
AddressType_DEFERRED_TRANSFER,
|
||||
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,
|
||||
DEFERRED_TRANSFER = AddressType_DEFERRED_TRANSFER,
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import { BackendTransaction } from '@entity/BackendTransaction'
|
||||
|
||||
import { TransactionDraft } from '@/graphql/input/TransactionDraft'
|
||||
|
||||
export class BackendTransactionFactory {
|
||||
public static createFromTransactionDraft(transactionDraft: TransactionDraft): BackendTransaction {
|
||||
const backendTransaction = BackendTransaction.create()
|
||||
backendTransaction.backendTransactionId = transactionDraft.backendTransactionId
|
||||
backendTransaction.typeId = transactionDraft.type
|
||||
backendTransaction.createdAt = new Date(transactionDraft.createdAt)
|
||||
return backendTransaction
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import { BackendTransaction } from '@entity/BackendTransaction'
|
||||
|
||||
import { getDataSource } from '@/typeorm/DataSource'
|
||||
|
||||
export const BackendTransactionRepository = getDataSource()
|
||||
.getRepository(BackendTransaction)
|
||||
.extend({})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user