Initial Commit
Some checks failed
Build and Test / build (16.x) (push) Failing after 12s
Build and Test / build (18.x) (push) Failing after 6s
Build and Test / build (20.x) (push) Failing after 6s

This commit is contained in:
2025-11-05 15:25:20 -05:00
commit 98ab2e05a7
25 changed files with 1953 additions and 0 deletions

8
.eslintignore Normal file
View File

@@ -0,0 +1,8 @@
node_modules/
dist/
dist-esm/
coverage/
*.js
*.mjs
*.d.ts
!jest.config.js

25
.eslintrc.json Normal file
View File

@@ -0,0 +1,25 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"project": "./tsconfig.json"
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier"
],
"plugins": ["@typescript-eslint", "prettier"],
"rules": {
"prettier/prettier": "error",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
},
"env": {
"node": true,
"es2020": true
}
}

View File

@@ -0,0 +1,44 @@
name: Build and Test
on:
push:
branches: [ master, main, develop ]
pull_request:
branches: [ master, main, develop ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build package
run: npm run build
- name: Upload coverage
uses: actions/upload-artifact@v3
if: matrix.node-version == '20.x'
with:
name: coverage
path: coverage/

View File

@@ -0,0 +1,33 @@
name: Publish to NPM Registry
on:
release:
types: [created]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20.x'
registry-url: 'https://git.dragonchain.com/api/packages/dragonchain/npm/'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build package
run: npm run build
- name: Publish to Gitea NPM registry
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

37
.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# Dependencies
node_modules/
package-lock.json
yarn.lock
# Build outputs
dist/
dist-esm/
*.tsbuildinfo
# Test coverage
coverage/
.nyc_output/
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Environment
.env
.env.local
.env.*.local
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Temp files
*.tmp
.cache/

9
.prettierrc.json Normal file
View File

@@ -0,0 +1,9 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always"
}

190
LICENSE Normal file
View File

@@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Support. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2025 Dragonchain
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

229
README.md Normal file
View File

@@ -0,0 +1,229 @@
# Dragonchain Node.js SDK
Official Node.js and TypeScript SDK for interacting with Dragonchain blockchain nodes.
## Features
- 🔒 **HMAC-SHA256 Authentication** - Secure request signing
- 📦 **Full TypeScript Support** - Complete type definitions included
- 🌐 **Dual Package** - Works with both ESM and CommonJS
-**Comprehensive Testing** - Thoroughly tested with Jest
- 🎯 **Modern Best Practices** - Built with latest Node.js standards
## Installation
```bash
npm install @dragonchain-inc/prime-sdk
```
Or with yarn:
```bash
yarn add @dragonchain-inc/prime-sdk
```
## Quick Start
```typescript
import { DragonchainSDK } from '@dragonchain-inc/prime-sdk';
// Initialize the SDK
const sdk = new DragonchainSDK(
'your-public-id',
'your-auth-key-id',
'your-auth-key',
'https://your-dragonchain-endpoint.com'
);
// Check system health
await sdk.system.health();
// Get system status
const status = await sdk.system.status();
console.log(`Chain ID: ${status.id}, Level: ${status.level}`);
// Create a transaction
const txnResponse = await sdk.transaction.create({
txn_type: 'my-transaction-type',
payload: JSON.stringify({ message: 'Hello Dragonchain' }),
tag: 'example-tag',
});
console.log(`Created transaction: ${txnResponse.transaction_id}`);
```
## Using Configuration Files
The SDK supports loading credentials from YAML configuration files:
```yaml
default: my-chain-id
chains:
- name: my-chain
publicId: my-chain-id
authKeyId: my-auth-key-id
authKey: my-auth-key
endpoint: https://mychain.dragonchain.com
```
```typescript
import { DragonchainSDK } from '@dragonchain-inc/prime-sdk';
import { loadConfig, getDefaultChain } from '@dragonchain-inc/prime-sdk';
// Load config from file
const config = loadConfig('~/.dragonchain/credentials.yaml');
const chain = getDefaultChain(config);
if (chain) {
const sdk = new DragonchainSDK(
chain.publicId,
chain.authKeyId,
chain.authKey,
chain.endpoint
);
}
```
## API Reference
### System
```typescript
// Check system health
await sdk.system.health();
// Get system status
const status = await sdk.system.status();
```
### Transactions
```typescript
// Create a transaction
const response = await sdk.transaction.create({
txn_type: 'my-type',
payload: JSON.stringify({ data: 'example' }),
tag: 'optional-tag',
});
// Create multiple transactions
const bulkResponse = await sdk.transaction.createBulk({
transactions: [
{ txn_type: 'type1', payload: 'data1' },
{ txn_type: 'type2', payload: 'data2' },
],
});
// Get a transaction by ID
const txn = await sdk.transaction.get('transaction-id');
// List all transactions
const txns = await sdk.transaction.list();
```
### Transaction Types
```typescript
// Create a transaction type
await sdk.transactionType.create({
txn_type: 'my-new-type',
});
// Get a transaction type
const txnType = await sdk.transactionType.get('my-type');
// List all transaction types
const types = await sdk.transactionType.list();
// Delete a transaction type
await sdk.transactionType.delete('my-type');
```
### Blocks
```typescript
// Get a block by ID
const block = await sdk.block.get('block-id');
```
## TypeScript Support
This SDK is written in TypeScript and includes complete type definitions:
```typescript
import {
DragonchainSDK,
TransactionCreateRequest,
TransactionCreateResponse,
SystemStatus,
} from '@dragonchain-inc/prime-sdk';
const sdk = new DragonchainSDK(
publicId,
authKeyId,
authKey,
baseURL
);
// Full type safety
const request: TransactionCreateRequest = {
txn_type: 'my-type',
payload: JSON.stringify({ foo: 'bar' }),
};
const response: TransactionCreateResponse = await sdk.transaction.create(request);
```
## Authentication
The SDK uses HMAC-SHA256 authentication with the following components:
1. **Public ID** - Your Dragonchain public identifier
2. **Auth Key ID** - Your authentication key identifier
3. **Auth Key** - Your secret authentication key
4. **Endpoint** - The base URL of your Dragonchain node
Each request is signed with:
- `Authorization` header: `DC1-HMAC-SHA256 {authKeyId}:{signature}`
- `Dragonchain` header: Your public ID
- `Timestamp` header: Unix timestamp of the request
## Development
```bash
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build the package
npm run build
# Lint the code
npm run lint
# Format the code
npm run format
```
## Requirements
- Node.js >= 16.0.0
- TypeScript >= 5.0.0 (for TypeScript projects)
## License
Apache-2.0
## Support
For issues and questions, please visit:
- Repository: https://git.dragonchain.com/dragonchain/dragonchain-node-sdk
- Documentation: https://dragonchain-core-docs.dragonchain.com
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.

185
examples/basic-usage.ts Normal file
View File

@@ -0,0 +1,185 @@
// noinspection JSUnusedLocalSymbols
/**
* Basic usage examples for Dragonchain Node.js SDK
*/
import { DragonchainSDK, loadConfig, getDefaultChain } from '@dragonchain-inc/prime-sdk';
// Example 1: Initialize SDK with direct credentials
// @ts-ignore
async function directInitialization() {
const sdk = new DragonchainSDK(
'your-public-id',
'your-auth-key-id',
'your-auth-key',
'https://your-dragonchain-endpoint.com'
);
// Check system health
await sdk.system.health();
console.log('System is healthy');
// Get system status
const status = await sdk.system.status();
console.log(`Chain ID: ${status.id}, Level: ${status.level}`);
}
// Example 2: Initialize SDK from config file
// @ts-ignore
async function configFileInitialization() {
// Load credentials from YAML file
const config = loadConfig('~/.dragonchain/credentials.yaml');
const chain = getDefaultChain(config);
if (!chain) {
throw new Error('No default chain found in config');
}
const sdk = new DragonchainSDK(chain.publicId, chain.authKeyId, chain.authKey, chain.endpoint);
return sdk;
}
// Example 3: Create a transaction
// @ts-ignore
async function createTransaction(sdk: DragonchainSDK) {
const response = await sdk.transaction.create({
txn_type: 'my-transaction-type',
payload: JSON.stringify({
message: 'Hello Dragonchain',
timestamp: new Date().toISOString(),
}),
tag: 'example-tag',
});
console.log(`Created transaction: ${response.transaction_id}`);
return response.transaction_id;
}
// Example 4: Create bulk transactions
// @ts-ignore
async function createBulkTransactions(sdk: DragonchainSDK) {
const response = await sdk.transaction.createBulk({
transactions: [
{
txn_type: 'type1',
payload: JSON.stringify({ data: 'transaction 1' }),
},
{
txn_type: 'type2',
payload: JSON.stringify({ data: 'transaction 2' }),
},
{
txn_type: 'type3',
payload: JSON.stringify({ data: 'transaction 3' }),
},
],
});
console.log(`Created ${response.transaction_ids.length} transactions`);
return response.transaction_ids;
}
// Example 5: Get a transaction
// @ts-ignore
async function getTransaction(sdk: DragonchainSDK, transactionId: string) {
const transaction = await sdk.transaction.get(transactionId);
console.log(`Transaction: ${transaction.header.txn_id}`);
console.log(`Type: ${transaction.header.txn_type}`);
console.log(`Block: ${transaction.header.block_id}`);
return transaction;
}
// Example 6: Create and manage transaction types
// @ts-ignore
async function manageTransactionTypes(sdk: DragonchainSDK) {
// Create a new transaction type
await sdk.transactionType.create({
txn_type: 'my-new-type',
});
console.log('Transaction type created');
// Get the transaction type
const txnType = await sdk.transactionType.get('my-new-type');
console.log(`Created at: ${txnType.created}`);
// List all transaction types
const types = await sdk.transactionType.list();
console.log(`Total transaction types: ${types.transactionTypes.length}`);
}
// Example 7: Query blocks
// @ts-ignore
async function queryBlocks(sdk: DragonchainSDK, blockId: string) {
const block = await sdk.block.get(blockId);
console.log(`Block ID: ${block.block_id}`);
console.log(`Timestamp: ${block.timestamp}`);
console.log(`Transactions: ${block.transactions.length}`);
return block;
}
// Example 8: Error handling
// @ts-ignore
async function errorHandlingExample(sdk: DragonchainSDK) {
try {
await sdk.transaction.get('non-existent-transaction-id');
} catch (error) {
if (error instanceof Error) {
console.error(`Error: ${error.message}`);
// Handle specific error cases
if (error.message.includes('404')) {
console.log('Transaction not found');
}
}
}
}
// Example 9: Complete workflow
// @ts-ignore
async function completeWorkflow() {
// Initialize SDK
const sdk = new DragonchainSDK(
process.env.DC_PUBLIC_ID || '',
process.env.DC_AUTH_KEY_ID || '',
process.env.DC_AUTH_KEY || '',
process.env.DC_ENDPOINT || ''
);
try {
// Check system health
await sdk.system.health();
console.log('✓ System healthy');
// Create a transaction type
await sdk.transactionType.create({
txn_type: 'workflow-example',
});
console.log('✓ Transaction type created');
// Create a transaction
const txnResponse = await sdk.transaction.create({
txn_type: 'workflow-example',
payload: JSON.stringify({
step: 'initialization',
timestamp: Date.now(),
}),
});
console.log(`✓ Transaction created: ${txnResponse.transaction_id}`);
// Wait a bit for the transaction to be processed
await new Promise((resolve) => setTimeout(resolve, 2000));
// Get the transaction
const transaction = await sdk.transaction.get(txnResponse.transaction_id);
console.log(`✓ Transaction verified in block: ${transaction.header.block_id}`);
console.log('\nWorkflow completed successfully!');
} catch (error) {
console.error('Workflow failed:', error);
throw error;
}
}
// Run examples (uncomment to execute)
// completeWorkflow().catch(console.error);

19
jest.config.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
transform: {
'^.+\\.ts$': 'ts-jest',
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts',
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
moduleFileExtensions: ['ts', 'js', 'json'],
verbose: true,
};

68
package.json Normal file
View File

@@ -0,0 +1,68 @@
{
"name": "@dragonchain-inc/prime-sdk",
"version": "1.0.2",
"description": "Official Dragonchain SDK for Node.js and TypeScript",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": [
"dist",
"src",
"README.md",
"LICENSE"
],
"scripts": {
"build": "tsup",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src --ext .ts",
"lint:fix": "eslint src --ext .ts --fix",
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
"clean": "rm -rf dist coverage",
"prepublishOnly": "npm run clean && npm run build && npm test"
},
"keywords": [
"dragonchain",
"blockchain",
"sdk",
"api",
"typescript",
"nodejs"
],
"author": "Dragonchain",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://git.dragonchain.com/dragonchain/dragonchain-node-sdk"
},
"engines": {
"node": ">=16.0.0"
},
"dependencies": {
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/jest": "^29.5.12",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.11.30",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"tsup": "^8.5.0",
"typescript": "^5.4.3"
}
}

21
src/block.ts Normal file
View File

@@ -0,0 +1,21 @@
/**
* Block module for querying Dragonchain blocks
*/
import { DragonchainClient } from './client';
import { Block } from './types';
export class BlockClient {
private client: DragonchainClient;
constructor(client: DragonchainClient) {
this.client = client;
}
/**
* Gets a block by ID
*/
async get(blockId: string): Promise<Block> {
return this.client.get<Block>(`/api/v1/block/${blockId}`);
}
}

207
src/client.ts Normal file
View File

@@ -0,0 +1,207 @@
/**
* HTTP Client with HMAC-SHA256 authentication for Dragonchain
*/
import * as crypto from 'crypto';
import * as https from 'https';
import * as http from 'http';
import { URL } from 'url';
export interface ClientConfig {
publicId: string;
authKeyId: string;
authKey: string;
baseURL: string;
timeout?: number;
}
export class DragonchainClient {
private readonly publicId: string;
private readonly authKeyId: string;
private readonly authKey: string;
private readonly baseURL: string;
private readonly timeout: number;
constructor(config: ClientConfig) {
this.publicId = config.publicId;
this.authKeyId = config.authKeyId;
this.authKey = config.authKey;
this.baseURL = config.baseURL.replace(/\/$/, ''); // Remove trailing slash
this.timeout = config.timeout || 30000; // Default 30 seconds
}
/**
* Creates HMAC message string for signing
*/
private createHmacMessage(
method: string,
path: string,
timestamp: string,
contentType: string,
body: Buffer
): string {
// Hash the body content
const contentHash = crypto.createHash('sha256').update(body).digest();
const b64Content = contentHash.toString('base64');
// Format: METHOD\nPATH\nPUBLIC_ID\nTIMESTAMP\nCONTENT_TYPE\nBASE64_CONTENT_HASH
return [method.toUpperCase(), path, this.publicId, timestamp, contentType, b64Content].join(
'\n'
);
}
/**
* Creates HMAC signature
*/
private createHmac(message: string): string {
const hmac = crypto.createHmac('sha256', this.authKey);
hmac.update(message);
return hmac.digest('base64');
}
/**
* Generates the Authorization header
*/
private generateAuthHeader(
method: string,
path: string,
timestamp: string,
contentType: string,
body: Buffer
): string {
const message = this.createHmacMessage(method, path, timestamp, contentType, body);
const signature = this.createHmac(message);
return `DC1-HMAC-SHA256 ${this.authKeyId}:${signature}`;
}
/**
* Performs an HTTP request
*/
private async doRequest<T>(
method: string,
path: string,
contentType: string,
body: unknown,
responseType?: 'json' | 'buffer'
): Promise<T> {
let bodyBuffer: Buffer;
// Prepare request body
if (body === null || body === undefined) {
bodyBuffer = Buffer.from('');
} else if (Buffer.isBuffer(body)) {
bodyBuffer = body;
} else if (typeof body === 'string') {
bodyBuffer = Buffer.from(body);
} else {
bodyBuffer = Buffer.from(JSON.stringify(body));
if (!contentType) {
contentType = 'application/json';
}
}
// Generate authentication headers
const timestamp = Math.floor(Date.now() / 1000).toString();
const authHeader = this.generateAuthHeader(method, path, timestamp, contentType, bodyBuffer);
// Parse URL
const fullURL = `${this.baseURL}${path}`;
const parsedURL = new URL(fullURL);
const isHttps = parsedURL.protocol === 'https:';
// Prepare request options
const options: https.RequestOptions = {
hostname: parsedURL.hostname,
port: parsedURL.port || (isHttps ? 443 : 80),
path: parsedURL.pathname + parsedURL.search,
method: method.toUpperCase(),
headers: {
Authorization: authHeader,
Dragonchain: this.publicId,
Timestamp: timestamp,
...(contentType && { 'Content-Type': contentType }),
'Content-Length': bodyBuffer.length,
},
timeout: this.timeout,
};
return new Promise<T>((resolve, reject) => {
const httpModule = isHttps ? https : http;
const req = httpModule.request(options, (res) => {
const chunks: Buffer[] = [];
res.on('data', (chunk: Buffer) => {
chunks.push(chunk);
});
res.on('end', () => {
const responseBody = Buffer.concat(chunks);
// Check for errors
if (res.statusCode && res.statusCode >= 400) {
const errorMessage = responseBody.toString('utf8').trim();
reject(new Error(`API error (status ${res.statusCode}): ${errorMessage}`));
return;
}
// Return response
if (responseType === 'buffer') {
resolve(responseBody as T);
} else if (responseBody.length > 0) {
try {
const parsed = JSON.parse(responseBody.toString('utf8')) as T;
resolve(parsed);
} catch (error) {
reject(new Error(`Failed to parse response: ${(error as Error).message}`));
}
} else {
resolve({} as T);
}
});
});
req.on('error', (error) => {
reject(new Error(`Request failed: ${error.message}`));
});
req.on('timeout', () => {
req.destroy();
reject(new Error(`Request timeout after ${this.timeout}ms`));
});
// Write body and end request
if (bodyBuffer.length > 0) {
req.write(bodyBuffer);
}
req.end();
});
}
/**
* Performs a GET request
*/
public async get<T>(path: string): Promise<T> {
return this.doRequest<T>('GET', path, '', null);
}
/**
* Performs a POST request
*/
public async post<T>(path: string, contentType: string, body: unknown): Promise<T> {
return this.doRequest<T>('POST', path, contentType, body);
}
/**
* Performs a PUT request
*/
public async put<T>(path: string, contentType: string, body: unknown): Promise<T> {
return this.doRequest<T>('PUT', path, contentType, body);
}
/**
* Performs a DELETE request
*/
public async delete<T>(path: string): Promise<T> {
return this.doRequest<T>('DELETE', path, '', null);
}
}

73
src/contract.ts Normal file
View File

@@ -0,0 +1,73 @@
/**
* Contract module for managing Dragonchain smart contracts
*/
import * as fs from 'fs';
import { DragonchainClient } from './client';
import {
ContentType,
SmartContractCreateRequest,
SmartContractUpdateRequest,
SmartContract,
ListResponse,
SuccessResponse,
} from './types';
export class ContractClient {
private client: DragonchainClient;
constructor(client: DragonchainClient) {
this.client = client;
}
/**
* Creates a new smart contract
*/
async create(request: SmartContractCreateRequest): Promise<SmartContract> {
return this.client.post<SmartContract>('/api/v1/contract', ContentType.JSON, request);
}
/**
* Gets a smart contract by ID
*/
async get(contractId: string): Promise<SmartContract> {
return this.client.get<SmartContract>(`/api/v1/contract/${contractId}`);
}
/**
* Lists all smart contracts
*/
async list(): Promise<ListResponse> {
return this.client.get<ListResponse>('/api/v1/contract');
}
/**
* Updates a smart contract
*/
async update(contractId: string, request: SmartContractUpdateRequest): Promise<SuccessResponse> {
return this.client.put<SuccessResponse>(
`/api/v1/contract/${contractId}`,
ContentType.JSON,
request
);
}
/**
* Uploads smart contract code
*/
async upload(contractId: string, filePath: string): Promise<SuccessResponse> {
const fileContent = fs.readFileSync(filePath);
return this.client.put<SuccessResponse>(
`/api/v1/contract/${contractId}/upload`,
ContentType.OCTET_STREAM,
fileContent
);
}
/**
* Deletes a smart contract
*/
async delete(contractId: string): Promise<SuccessResponse> {
return this.client.delete<SuccessResponse>(`/api/v1/contract/${contractId}`);
}
}

109
src/credentials.ts Normal file
View File

@@ -0,0 +1,109 @@
/**
* Credentials module for loading and managing Dragonchain configuration
*/
import * as fs from 'fs';
import * as path from 'path';
import * as yaml from 'js-yaml';
import { homedir } from 'os';
/**
* Configuration for a single Dragonchain
*/
export interface ChainConfig {
name: string;
publicId: string;
authKeyId: string;
authKey: string;
endpoint: string;
}
/**
* Complete configuration structure
*/
export interface Config {
default: string;
chains: ChainConfig[];
}
/**
* Expands file paths with home directory and environment variables
*/
function expandPath(filePath: string): string {
// Expand environment variables
let expanded = filePath.replace(/\$\{([^}]+)\}/g, (_, variable: string) => {
return process.env[variable] || '';
});
// Handle tilde for home directory
if (expanded.startsWith('~/')) {
expanded = path.join(homedir(), expanded.slice(2));
} else if (expanded === '~') {
expanded = homedir();
}
return expanded;
}
/**
* Loads configuration from a YAML file
*/
export function loadConfig(filePath: string): Config {
const expandedPath = expandPath(filePath);
if (!fs.existsSync(expandedPath)) {
throw new Error(`Config file not found: ${expandedPath}`);
}
const fileContent = fs.readFileSync(expandedPath, 'utf8');
const config = yaml.load(fileContent) as Config;
if (!config.chains || !Array.isArray(config.chains)) {
throw new Error('Invalid config: missing or invalid chains array');
}
return config;
}
/**
* Loads configuration from a YAML string
*/
export function loadConfigFromString(yamlContent: string): Config {
const config = yaml.load(yamlContent) as Config;
if (!config.chains || !Array.isArray(config.chains)) {
throw new Error('Invalid config: missing or invalid chains array');
}
return config;
}
/**
* Gets the default chain configuration
*/
export function getDefaultChain(config: Config): ChainConfig | undefined {
return config.chains.find((chain) => chain.publicId === config.default);
}
/**
* Gets a chain configuration by public ID
*/
export function getChainByPublicId(config: Config, publicId: string): ChainConfig | undefined {
return config.chains.find((chain) => chain.publicId === publicId);
}
/**
* Lists all chain names in the configuration
*/
export function listChains(config: Config): string[] {
return config.chains.map((chain) => chain.name);
}
/**
* Saves configuration to a YAML file
*/
export function saveConfig(config: Config, filePath: string): void {
const expandedPath = expandPath(filePath);
const yamlContent = yaml.dump(config);
fs.writeFileSync(expandedPath, yamlContent, 'utf8');
}

77
src/index.ts Normal file
View File

@@ -0,0 +1,77 @@
/**
* Dragonchain SDK for Node.js and TypeScript
*
* Official SDK for interacting with Dragonchain blockchain nodes
*/
import { DragonchainClient, ClientConfig } from './client';
import { TransactionClient } from './transaction';
import { TransactionTypeClient } from './transactionType';
import { ContractClient } from './contract';
import { BlockClient } from './block';
import { SystemClient } from './system';
/**
* Main Dragonchain SDK class
*/
export class DragonchainSDK {
private client: DragonchainClient;
public readonly transaction: TransactionClient;
public readonly transactionType: TransactionTypeClient;
public readonly contract: ContractClient;
public readonly block: BlockClient;
public readonly system: SystemClient;
/**
* Creates a new Dragonchain SDK instance
*
* @param publicId - Your Dragonchain public ID
* @param authKeyId - Your authentication key ID
* @param authKey - Your authentication key
* @param baseURL - The base URL of your Dragonchain node
* @param timeout - Optional request timeout in milliseconds (default: 30000)
*/
constructor(
publicId: string,
authKeyId: string,
authKey: string,
baseURL: string,
timeout?: number
) {
const config: ClientConfig = {
publicId,
authKeyId,
authKey,
baseURL,
timeout,
};
this.client = new DragonchainClient(config);
this.transaction = new TransactionClient(this.client);
this.transactionType = new TransactionTypeClient(this.client);
this.contract = new ContractClient(this.client);
this.block = new BlockClient(this.client);
this.system = new SystemClient(this.client);
}
/**
* Gets the underlying HTTP client
*/
public getClient(): DragonchainClient {
return this.client;
}
}
// Export all types and modules
export * from './types';
export * from './credentials';
export { DragonchainClient, ClientConfig } from './client';
export { TransactionClient } from './transaction';
export { TransactionTypeClient } from './transactionType';
export { ContractClient } from './contract';
export { BlockClient } from './block';
export { SystemClient } from './system';
// Default export
export default DragonchainSDK;

28
src/system.ts Normal file
View File

@@ -0,0 +1,28 @@
/**
* System module for Dragonchain system operations
*/
import { DragonchainClient } from './client';
import { SystemStatus } from './types';
export class SystemClient {
private client: DragonchainClient;
constructor(client: DragonchainClient) {
this.client = client;
}
/**
* Checks system health
*/
async health(): Promise<void> {
await this.client.get<void>('/api/v1/health');
}
/**
* Gets system status
*/
async status(): Promise<SystemStatus> {
return this.client.get<SystemStatus>('/api/v1/status');
}
}

58
src/transaction.ts Normal file
View File

@@ -0,0 +1,58 @@
/**
* Transaction module for managing Dragonchain transactions
*/
import { DragonchainClient } from './client';
import {
ContentType,
TransactionCreateRequest,
TransactionCreateResponse,
TransactionBulkRequest,
TransactionBulkResponse,
Transaction,
ListTransactionsResponse,
} from './types';
export class TransactionClient {
private client: DragonchainClient;
constructor(client: DragonchainClient) {
this.client = client;
}
/**
* Creates a new transaction
*/
async create(request: TransactionCreateRequest): Promise<TransactionCreateResponse> {
return this.client.post<TransactionCreateResponse>(
'/api/v1/transaction',
ContentType.JSON,
request
);
}
/**
* Creates multiple transactions in bulk
*/
async createBulk(request: TransactionBulkRequest): Promise<TransactionBulkResponse> {
return this.client.post<TransactionBulkResponse>(
'/api/v1/transaction/bulk',
ContentType.JSON,
request
);
}
/**
* Gets a transaction by ID
*/
async get(transactionId: string): Promise<Transaction> {
return this.client.get<Transaction>(`/api/v1/transaction/${transactionId}`);
}
/**
* Lists all transactions
*/
async list(): Promise<ListTransactionsResponse> {
return this.client.get<ListTransactionsResponse>('/api/v1/transaction/');
}
}

53
src/transactionType.ts Normal file
View File

@@ -0,0 +1,53 @@
/**
* Transaction Type module for managing Dragonchain transaction types
*/
import { DragonchainClient } from './client';
import {
ContentType,
TransactionTypeCreateRequest,
TransactionTypeCreateResponse,
TransactionType,
TransactionListResponse,
SuccessResponse,
} from './types';
export class TransactionTypeClient {
private client: DragonchainClient;
constructor(client: DragonchainClient) {
this.client = client;
}
/**
* Creates a new transaction type
*/
async create(request: TransactionTypeCreateRequest): Promise<TransactionTypeCreateResponse> {
return this.client.post<TransactionTypeCreateResponse>(
'/api/v1/transaction-type',
ContentType.JSON,
request
);
}
/**
* Gets a transaction type by name
*/
async get(txnType: string): Promise<TransactionType> {
return this.client.get<TransactionType>(`/api/v1/transaction-type/${txnType}`);
}
/**
* Lists all transaction types
*/
async list(): Promise<TransactionListResponse> {
return this.client.get<TransactionListResponse>('/api/v1/transaction-types');
}
/**
* Deletes a transaction type
*/
async delete(txnType: string): Promise<SuccessResponse> {
return this.client.delete<SuccessResponse>(`/api/v1/transaction-type/${txnType}`);
}
}

162
src/types.ts Normal file
View File

@@ -0,0 +1,162 @@
/**
* Type definitions for Dragonchain SDK
*/
export const ContentType = {
JSON: 'application/json',
OCTET_STREAM: 'application/octet-stream',
} as const;
// Transaction Types
export interface TransactionCreateRequest {
version?: string;
txn_type: string;
payload: string | Record<string, unknown>;
tag?: string;
}
export interface TransactionCreateResponse {
transaction_id: string;
}
export interface TransactionBulkRequest {
transactions: TransactionCreateRequest[];
}
export interface TransactionBulkResponse {
transaction_ids: string[];
}
export interface TransactionHeader {
tag: string;
dc_id: string;
txn_id: string;
invoker: string;
block_id: string;
txn_type: string;
timestamp: string;
}
export interface TransactionProof {
full: string;
stripped: string;
}
export interface Transaction {
version: string;
header: TransactionHeader;
proof: TransactionProof;
payload: string;
}
export interface ListTransactionsResponse {
transactions: Transaction[];
}
// Transaction Type Types
export interface TransactionTypeCreateRequest {
version?: string;
txn_type: string;
}
export interface TransactionTypeCreateResponse {
success: boolean;
}
export interface TransactionType {
version: string;
created: number;
modified: number;
txn_type: string;
contract_id?: string;
custom_indexes: unknown[];
active_since_block: string;
}
export interface TransactionListResponse {
transactionTypes: TransactionType[];
}
// Smart Contract Types
export interface SmartContractCreateRequest {
environment: string;
transactionType: string;
executionOrder: string;
environmentVariables?: Record<string, string>;
secrets?: Record<string, string>;
}
export interface SmartContractUpdateRequest {
version?: string;
enabled: boolean;
environmentVariables?: Record<string, string>;
secrets?: Record<string, string>;
}
export interface SmartContractExecutionInfo {
type: string;
executablePath?: string;
executableWorkingDirectory?: string;
executableHash?: string;
}
export interface SmartContract {
id: string;
created: number;
modified: number;
version: string;
environment: string;
transactionType: string;
executionOrder: string;
executionInfo?: SmartContractExecutionInfo;
envVars: Record<string, string>;
secrets: string[];
}
// Block Types
export interface BlockProof {
scheme: string;
proof: string;
nonce?: number;
}
export interface Block {
version: string;
block_id: string;
timestamp: string;
prev_id: string;
prev_proof: string;
transactions: string[];
proof: BlockProof;
}
// System Types
export interface SystemStatus {
id: string;
level: number;
url: string;
hashAlgo: string;
scheme: string;
version: string;
encryptionAlgo: string;
indexingEnabled: boolean;
// Level 5 Node specific fields
funded?: string;
broadcastInterval?: string;
network?: string;
interchainWallet?: string;
}
// Generic Response Types
export interface SuccessResponse {
success: boolean;
}
export interface ErrorResponse {
error: string;
}
export interface ListResponse {
items: unknown[];
total_count: number;
}

88
tests/client.test.ts Normal file
View File

@@ -0,0 +1,88 @@
/**
* Tests for HTTP client with HMAC authentication
*/
import * as crypto from 'crypto';
import { DragonchainClient } from '../src/client';
describe('DragonchainClient', () => {
const testConfig = {
publicId: 'test-public-id',
authKeyId: 'test-auth-key-id',
authKey: 'test-auth-key',
baseURL: 'https://test.dragonchain.com',
};
let client: DragonchainClient;
beforeEach(() => {
client = new DragonchainClient(testConfig);
});
describe('constructor', () => {
it('should create a client with correct configuration', () => {
expect(client).toBeDefined();
expect(client.get).toBeDefined();
});
it('should remove trailing slash from baseURL', () => {
const clientWithSlash = new DragonchainClient({
...testConfig,
baseURL: 'https://test.dragonchain.com/',
});
expect(clientWithSlash).toBeDefined();
});
});
describe('HMAC authentication', () => {
it('should generate correct HMAC signature', () => {
// Test HMAC generation by creating a mock scenario
const method = 'POST';
const path = '/api/v1/transaction';
const timestamp = '1234567890';
const contentType = 'application/json';
const body = Buffer.from(JSON.stringify({ test: 'data' }));
// Calculate expected values
const contentHash = crypto.createHash('sha256').update(body).digest('base64');
const message = [
method.toUpperCase(),
path,
testConfig.publicId,
timestamp,
contentType,
contentHash,
].join('\n');
const expectedHmac = crypto
.createHmac('sha256', testConfig.authKey)
.update(message)
.digest('base64');
// The auth header should follow the format: DC1-HMAC-SHA256 {authKeyId}:{signature}
const expectedAuthHeader = `DC1-HMAC-SHA256 ${testConfig.authKeyId}:${expectedHmac}`;
// We can't directly test the private method, but we verify the format is correct
expect(expectedAuthHeader).toContain('DC1-HMAC-SHA256');
expect(expectedAuthHeader).toContain(testConfig.authKeyId);
});
});
describe('request methods', () => {
it('should have get method', () => {
expect(typeof client.get).toBe('function');
});
it('should have post method', () => {
expect(typeof client.post).toBe('function');
});
it('should have put method', () => {
expect(typeof client.put).toBe('function');
});
it('should have delete method', () => {
expect(typeof client.delete).toBe('function');
});
});
});

105
tests/credentials.test.ts Normal file
View File

@@ -0,0 +1,105 @@
/**
* Tests for credentials module
*/
import * as fs from 'fs';
import * as path from 'path';
import { loadConfig, loadConfigFromString, getDefaultChain, getChainByPublicId, listChains } from '../src/credentials';
describe('Credentials Module', () => {
const testYaml = `
default: test-chain-id
chains:
- name: test-chain
publicId: test-chain-id
authKeyId: test-auth-key-id
authKey: test-auth-key
endpoint: https://test.dragonchain.com
- name: another-chain
publicId: another-chain-id
authKeyId: another-auth-key-id
authKey: another-auth-key
endpoint: https://another.dragonchain.com
`;
describe('loadConfigFromString', () => {
it('should load config from YAML string', () => {
const config = loadConfigFromString(testYaml);
expect(config.default).toBe('test-chain-id');
expect(config.chains).toHaveLength(2);
expect(config.chains[0].name).toBe('test-chain');
});
it('should throw error for invalid YAML', () => {
expect(() => loadConfigFromString('invalid: [yaml')).toThrow();
});
it('should throw error for missing chains array', () => {
expect(() => loadConfigFromString('default: test')).toThrow('Invalid config: missing or invalid chains array');
});
});
describe('loadConfig', () => {
const testConfigPath = path.join(__dirname, 'test-config.yaml');
beforeEach(() => {
fs.writeFileSync(testConfigPath, testYaml);
});
afterEach(() => {
if (fs.existsSync(testConfigPath)) {
fs.unlinkSync(testConfigPath);
}
});
it('should load config from file', () => {
const config = loadConfig(testConfigPath);
expect(config.default).toBe('test-chain-id');
expect(config.chains).toHaveLength(2);
});
it('should throw error for non-existent file', () => {
expect(() => loadConfig('/non/existent/path.yaml')).toThrow('Config file not found');
});
});
describe('getDefaultChain', () => {
it('should return the default chain', () => {
const config = loadConfigFromString(testYaml);
const defaultChain = getDefaultChain(config);
expect(defaultChain).toBeDefined();
expect(defaultChain?.name).toBe('test-chain');
expect(defaultChain?.publicId).toBe('test-chain-id');
});
it('should return undefined if default not found', () => {
const config = loadConfigFromString(testYaml);
config.default = 'non-existent';
const defaultChain = getDefaultChain(config);
expect(defaultChain).toBeUndefined();
});
});
describe('getChainByPublicId', () => {
it('should return chain by public ID', () => {
const config = loadConfigFromString(testYaml);
const chain = getChainByPublicId(config, 'another-chain-id');
expect(chain).toBeDefined();
expect(chain?.name).toBe('another-chain');
});
it('should return undefined if chain not found', () => {
const config = loadConfigFromString(testYaml);
const chain = getChainByPublicId(config, 'non-existent');
expect(chain).toBeUndefined();
});
});
describe('listChains', () => {
it('should return all chain names', () => {
const config = loadConfigFromString(testYaml);
const names = listChains(config);
expect(names).toEqual(['test-chain', 'another-chain']);
});
});
});

91
tests/sdk.test.ts Normal file
View File

@@ -0,0 +1,91 @@
/**
* Tests for main SDK
*/
import { DragonchainSDK } from '../src/index';
describe('DragonchainSDK', () => {
const testConfig = {
publicId: 'test-public-id',
authKeyId: 'test-auth-key-id',
authKey: 'test-auth-key',
baseURL: 'https://test.dragonchain.com',
};
let sdk: DragonchainSDK;
beforeEach(() => {
sdk = new DragonchainSDK(
testConfig.publicId,
testConfig.authKeyId,
testConfig.authKey,
testConfig.baseURL
);
});
describe('initialization', () => {
it('should create SDK instance', () => {
expect(sdk).toBeDefined();
});
it('should initialize all client modules', () => {
expect(sdk.transaction).toBeDefined();
expect(sdk.transactionType).toBeDefined();
expect(sdk.contract).toBeDefined();
expect(sdk.block).toBeDefined();
expect(sdk.system).toBeDefined();
});
it('should provide access to underlying client', () => {
const client = sdk.getClient();
expect(client).toBeDefined();
});
});
describe('module methods', () => {
it('should have transaction module with correct methods', () => {
expect(typeof sdk.transaction.create).toBe('function');
expect(typeof sdk.transaction.createBulk).toBe('function');
expect(typeof sdk.transaction.get).toBe('function');
expect(typeof sdk.transaction.list).toBe('function');
});
it('should have transactionType module with correct methods', () => {
expect(typeof sdk.transactionType.create).toBe('function');
expect(typeof sdk.transactionType.get).toBe('function');
expect(typeof sdk.transactionType.list).toBe('function');
expect(typeof sdk.transactionType.delete).toBe('function');
});
it('should have contract module with correct methods', () => {
expect(typeof sdk.contract.create).toBe('function');
expect(typeof sdk.contract.get).toBe('function');
expect(typeof sdk.contract.list).toBe('function');
expect(typeof sdk.contract.update).toBe('function');
expect(typeof sdk.contract.upload).toBe('function');
expect(typeof sdk.contract.delete).toBe('function');
});
it('should have block module with correct methods', () => {
expect(typeof sdk.block.get).toBe('function');
});
it('should have system module with correct methods', () => {
expect(typeof sdk.system.health).toBe('function');
expect(typeof sdk.system.status).toBe('function');
});
});
describe('timeout configuration', () => {
it('should accept custom timeout', () => {
const customSdk = new DragonchainSDK(
testConfig.publicId,
testConfig.authKeyId,
testConfig.authKey,
testConfig.baseURL,
60000
);
expect(customSdk).toBeDefined();
});
});
});

24
tsconfig.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"]
}

10
tsup.config.ts Normal file
View File

@@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
splitting: false,
sourcemap: true,
clean: true,
});