# Overview

ArDrive Core is a TypeScript library that contains the essential back end application features to support the ArDrive CLI and Desktop apps, such as file management, Permaweb upload/download, wallet management, and other common functions.

Engage with the community in Discord (opens new window) for more information.

# Integrating with ArDrive Core

To add the ArDrive Core library to your project, simply add it as a dependency:

yarn add ardrive-core-js

The recommended approach for integrating with ArDrive Core as a dependency in your project is to construct and use the methods provided on the ArDrive class. Developers can use the convenience function arDriveFactory to construct the ArDrive class.

Below are a few common examples of interacting with Core:

import { readJWKFile, arDriveFactory } from 'ardrive-core-js';

// Read wallet from file
const myWallet = readJWKFile('/path/to/wallet');

// Construct ArDrive class
const arDrive = arDriveFactory({ wallet: myWallet });

// Create a public drive and its root folder
const createDriveResult = await arDrive.createPublicDrive({ driveName: 'My-Drive' });
import { wrapFileOrFolder, EID } from 'ardrive-core-js';

// Wrap file for upload
const wrappedEntity = wrapFileOrFolder('path/to/file');

// Construct a safe Entity ID Type
const destFolderId = EID('10108b54a-eb5e-4134-8ae2-a3946a428ec7');

// Upload a public file to destination folder
const uploadFileResult = await arDrive.uploadAllEntities({
    entitiesToUpload: [{ wrappedEntity, destFolderId }]
});
import { deriveDriveKey } from 'ardrive-core-js';

// Derive a private drive key from password, wallet, and drive ID
const driveKey = await deriveDriveKey(
    'mySecretPassWord',
    '12345674a-eb5e-4134-8ae2-a3946a428ec7',
    JSON.stringify((myWallet as JWKWallet).getPrivateKey())
);

// Create a private folder
const createFolderResult = await arDrive.createPrivateFolder({
    folderName: 'My New Private Folder',
    driveKey,
    parentFolderId: EID('47162534a-eb5e-4134-8ae2-a3946a428ec7')
});
import { wrapFileOrFolder, EntityKey, EID } from 'ardrive-core-js';

// Derive a private drive key from raw drive key string
const driveKey = new EntityKey(Buffer.from('MyAwesomeDriveKeyZZZZZZZZZZZZZZZZZZZZFAKE/s', 'base64'));

// Wrap folder and all of its contents for upload
const wrappedFolder = wrapFileOrFolder('path/to/folder');

// Upload a private folder and all its contents
const uploadFileResult = await arDrive.uploadAllEntities({
    entitiesToUpload: [
        {
            wrappedEntity: wrappedFolder,
            destFolderId: EID('76543214a-eb5e-4134-8ae2-a3946a428ec7'),
            driveKey
        },
        // And some other public file to a different destination 🤯
        {
          wrappedEntity: someOtherWrappedFile
          destFolderId: EID('675489321-eb5e-4134-8ae2-a3946a428ec7')
        }
    ]
});
// Upload ArFS Entities To Turbo (BETA)
// The presence of `turboSettings` in `arDriveFactory` enables sending to Turbo
const arDrive = arDriveFactory({ wallet: myWallet, turboSettings: {} });

const uploadFileResult = await arDrive.uploadAllEntities({
    entitiesToUpload: [{ wrappedEntity, destFolderId }],
});

# Development Environment Setup

We use nvm to manage our Node engine version and, if necessary, to install an npm version that we can then use to install Yarn.

Note for Windows: We recommend using WSL for setting up NVM on Windows using the instructions described here (opens new window) then continue the steps below.

  1. Install nvm using their instructions (opens new window).

  2. We use Node 18.x -- ensure that the correct Node version is installed and activated by performing nvm install and then nvm use

  3. We use Yarn 3.x please follow the installation guidelines here (opens new window)

  4. We use husky 6.x to manage the git commit hooks that help to improve the quality of our commits. Please run: yarn husky install to enable git hooks for this local repository. Without doing so, you risk committing non-compliant code to the repository.

  5. Install all node package dependencies by running yarn install --check-cache

To ensure your environment is compatible, we also recommend the following VSCode extensions:

# Building the Library

Simply run yarn build. This will clean the project and compile the TypeScript library.

# Testing the Library

This library is setup for Mocha (opens new window) testing with Chai (opens new window) and Sinon (opens new window). Configuration for Mocha can be found in .mocharc.js

To run all of the tests use:

yarn test

To run a specific test, use Mocha's grep (opens new window) command. This will cause Mocha to only run the tests that contain the provided RegExp.

The -g command will only match the characters used in the describe() and it() title arguments. It will not match files names or folders.

For example:

yarn test -g 'My specific unit test'

Will run this test:

describe('My specific unit test', () => {
    it('functions correctly', () => {
        // ...
    });
});

# Coverage

Istanbul.js (nyc) (opens new window) has been added for code coverage reporting. Configuration for the nyc package can be found in nyc.config.js

On each yarn test command, nyc will output a code coverage summary in the terminal. In addition, a more detailed HTML version will output to the /coverage directory. Simply run /coverage/index.html in your browser to view the HTML version.

Alternatively, you can view a verbose coverage output in the terminal by running:

yarn coverage

# Adding tests

There are many different syntax options available with the Chai library, which can be found in their documentation (opens new window). For examples on unit testing, visit src/example.test.ts, and for integration testing: tests/example.test.ts.

Unit tests should be located adjacent (or right next to) the file they are referencing. They should also be named the same with the .test.ts extension. In contrast, integration tests will live in the /tests directory.

For example:

ardrive-core-js/
├── src
│   ├── fileToTest.ts
│   └── fileToTest.test.ts   <-- Unit test
└── tests
    └── bestApi.test.ts   <----- Integration test

# Using Sinon

Sinon can be used to create spies, mocks, fakes, stubs, and more. There are some basic examples of using the library shown in the example test files shared above.

For more information on what you can do with Sinon, visit their documentation (opens new window).

# Debugging with Power-Assert

Power-Assert (opens new window) is setup as another testing tool. The library can be used to provide a very detailed output of your failing test cases. This can become super useful while debugging a test.

To use this tool, it must be imported using this syntax:

import assert = require('assert');

Then use assert in your error throwing test case. Commenting out the Chai assertion will produce a cleaner output:

// expect(failingOutput).to.equal(expectedOutput);
assert(failingOutput === expectedOutput);

And finally, to view the detailed error messages in your terminal:

yarn power-assert -g 'My test case'

# Progress Logging of Transaction Uploads

Progress logging of transaction uploads to stderr can be enabled by setting the ARDRIVE_PROGRESS_LOG environment variable to 1:

Uploading file transaction 1 of total 2 transactions...
Transaction _GKQasQX194a364Hph8Oe-oku1AdfHwxWOw9_JC1yjc Upload Progress: 0%
Transaction _GKQasQX194a364Hph8Oe-oku1AdfHwxWOw9_JC1yjc Upload Progress: 35%
Transaction _GKQasQX194a364Hph8Oe-oku1AdfHwxWOw9_JC1yjc Upload Progress: 66%
Transaction _GKQasQX194a364Hph8Oe-oku1AdfHwxWOw9_JC1yjc Upload Progress: 100%
Uploading file transaction 2 of total 2 transactions...
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 0%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 13%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 28%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 42%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 60%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 76%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 91%
Transaction nA1stCdTkuf290k0qsqvmJ78isEC0bwgrAi3D8Cl1LU Upload Progress: 100%

# Persistent Caching of ArFS Entity Metadata

To avoid redundant requests to the Arweave network for immutable ArFS entity metadata, a persistent file cache is created and maintained at:

Windows: <os.homedir()>/ardrive-caches/metadata
Non-Windows: <os.homedir()>/.ardrive/caches/metadata

The XDG_CACHE_HOME environment variable is honored, where applicable, and will be used in place of os.homedir() in the scenarios described above.

Metadata cache logging to stderr can be enabled by setting the ARDRIVE_CACHE_LOG environment variable to 1.

Cache performance is UNDEFINED for multi-process scenarios, but is presumed to be generally usable.

The cache can be manually cleared safely at any time that any integrating app is not in operation.

# Applying Custom MetaData to ArFS File Transactions

Custom metadata can be attached to ArFS File Transactions. Metadata can be applied to either the GQL tags on the MetaData Transaction, the MetaData Transaction's Data JSON, or both.

All custom tags can be accessed by using by using ArDrive class read methods such as getPublicFile, getPrivateFile, listPrivateFolder, etc.

const arDrive = arDriveAnonymousFactory({});
const fileInfo = await arDrive.getPublicFile({ fileId });
const myMetaDataGqlTags = fileInfo.customMetaDataGqlTags;
const myMetaDataJsonFields = fileInfo.customMetaDataJson;

When the custom metadata is attached to the MetaData Transaction's GQL tags, they will become visible on any Arweave GQL gateway and also third party tools that read GQL data.

When these tags are added to the MetaData Transaction's Data JSON they can be read by downloading the JSON data directly from https://arweave.net/<metadata tx id>.

To add this custom metadata to your file metadata transactions, users can pass an object containing custom tags when wrapping content to upload:

const fileToUpload = wrapFileOrFolder(
    'path/to/file/on/system', // File or Folder Path
    'application/custom-content-type', // Custom Content Type
    customMetaData: { // Custom MetaData
        metaDataJson: { ['My-Custom-Tag-Name']: 'Single-Custom-Value' },
        metaDataGqlTags: {
            ['Another-Custom-Tag']: ['First-Custom-Value', 'Second-Custom-Value', 'Third-Custom-Value']
        }
    }
);