diff --git a/.pnp.js b/.pnp.js
index b54784ac..5a96f6bb 100755
--- a/.pnp.js
+++ b/.pnp.js
@@ -48,7 +48,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/source-map-support", "npm:0.5.4"],
["@typescript-eslint/eslint-plugin", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"],
["@typescript-eslint/parser", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"],
- ["ardrive-core-js", "npm:1.7.0"],
+ ["ardrive-core-js", "npm:1.8.0"],
["arweave", "npm:1.10.18"],
["axios", "npm:0.21.1"],
["chai", "npm:4.3.4"],
@@ -1879,7 +1879,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/source-map-support", "npm:0.5.4"],
["@typescript-eslint/eslint-plugin", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"],
["@typescript-eslint/parser", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"],
- ["ardrive-core-js", "npm:1.7.0"],
+ ["ardrive-core-js", "npm:1.8.0"],
["arweave", "npm:1.10.18"],
["axios", "npm:0.21.1"],
["chai", "npm:4.3.4"],
@@ -1905,10 +1905,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
}]
]],
["ardrive-core-js", [
- ["npm:1.7.0", {
- "packageLocation": "./.yarn/cache/ardrive-core-js-npm-1.7.0-1fc3cbf016-3223964ec9.zip/node_modules/ardrive-core-js/",
+ ["npm:1.8.0", {
+ "packageLocation": "./.yarn/cache/ardrive-core-js-npm-1.8.0-5a3b702d23-091a457022.zip/node_modules/ardrive-core-js/",
"packageDependencies": [
- ["ardrive-core-js", "npm:1.7.0"],
+ ["ardrive-core-js", "npm:1.8.0"],
["@alexsasharegan/simple-cache", "npm:3.3.3"],
["arbundles", "npm:0.5.5"],
["arweave", "npm:1.10.18"],
diff --git a/.yarn/cache/ardrive-core-js-npm-1.7.0-1fc3cbf016-3223964ec9.zip b/.yarn/cache/ardrive-core-js-npm-1.8.0-5a3b702d23-091a457022.zip
similarity index 53%
rename from .yarn/cache/ardrive-core-js-npm-1.7.0-1fc3cbf016-3223964ec9.zip
rename to .yarn/cache/ardrive-core-js-npm-1.8.0-5a3b702d23-091a457022.zip
index ad6cff9c..d1622dc5 100644
Binary files a/.yarn/cache/ardrive-core-js-npm-1.7.0-1fc3cbf016-3223964ec9.zip and b/.yarn/cache/ardrive-core-js-npm-1.8.0-5a3b702d23-091a457022.zip differ
diff --git a/README.md b/README.md
index 0273c799..cff92ac2 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,8 @@ ardrive upload-file --wallet-file /path/to/my/wallet.json --parent-folder-id "f0
12. [Moving Files](#moving-files)
13. [Uploading Manifests](#uploading-manifests)
14. [Hosting a Webpage with Manifest](#hosting-a-webpage-with-manifest)
+ 15. [Uploading With a Custom Content Type](#custom-content-type)
+ 16. [Uploading a Custom Manifest](#custom-manifest)
7. [Other Utility Operations](#other-utility-operations)
1. [Monitoring Transactions](#monitoring-transactions)
2. [Dealing With Network Congestion](#dealing-with-network-congestion)
@@ -251,6 +253,7 @@ To ensure your environment is compatible, we also recommend the following VSCode
**Number of files in a bulk upload:** Theoretically unlimited
**Max individual file size**: 2GB (Node.js limitation)
+**Max file name length**: 255 bytes
**Max ANS-104 bundled transaction size:** Not yet implemented. 2GB per bundle. App will handle creating multiple bundles.
**Max ANS-104 data item counts per bundled transaction:** Not yet implemented. Also not adequately specified, though a very large number of data items per bundle is associated with increased rates of GQL indexing failure.
@@ -959,6 +962,8 @@ If you'd like to preview the contents of your manifest before uploading, you can
ardrive create-manifest -w /path/to/wallet -f "6c312b3e-4778-4a18-8243-f2b346f5e7cb" --dry-run | jq '{manifest}.manifest'
```
+
+
```json
{
"manifest": "arweave/paths",
@@ -1033,6 +1038,50 @@ In the return output, the top link will be a link to the deployed web app:
This is effectively hosting a web app with ArDrive. Check out the ArDrive Price Calculator React App hosted as an [ArDrive Manifest][example-manifest-webpage].
+### Uploading With a Custom Content Type
+
+Each file uploaded to the Arweave network receives a `"Content-Type"` GraphQL tag that contains the MIME type for the file. The gateway will use this content type to determine how to serve that file's data transaction at the `arweave.net/{data tx id}` endpoint.
+
+By default, the CLI will attempt to derive this content type from the file extension of the provided file. In most cases, the content type that is derived will be correct and the gateway will properly serve the file.
+
+The CLI also provides the option for users to upload files with a custom content type using the `--content-type` flag:
+
+```shell
+ardrive upload-file --content-type "application/json" --local-path /path/to/file --parent-folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" -w /path/to/wallet.json
+```
+
+It is currently possible to set this value to any given string, but the gateway will still only serve valid content types. Check out this list of commonly used MIME types to ensure you're providing a valid content type: [Common MIME types][mozilla-mime-types].
+
+Note: In the case of multi-file uploads or recursive folder uploads, setting this `--content-type` flag will set the provided custom content type on EVERY file entity within a given upload.
+
+### Uploading a Custom Manifest
+
+Using the custom content type feature, it is possible for users to upload their own custom manifests. The Arweave gateways use this special content type in order to identify an uploaded file as a manifest:
+
+```shell
+application/x.arweave-manifest+json
+```
+
+In addition to this content type, the manifest must also adhere to the [correct JSON structure](#manifest-json) of an Arweave manifest. A user can create their own manifest from scratch, or start by piping a generated manifest to a JSON file and editing it to their specifications:
+
+```shell
+ardrive create-manifest -w /path/to/wallet -f "6c312b3e-4778-4a18-8243-f2b346f5e7cb" --dry-run | jq '{manifest}.manifest' > my-custom-manifest.json
+```
+
+After editing the generated manifest, simply perform an `upload-file` command with the custom Arweave manifest content type to any PUBLIC folder:
+
+```shell
+ardrive upload-file --content-type "application/x.arweave-manifest+json" --local-path my-custom-manifest.json --parent-folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" -w /path/to/wallet.json
+```
+
+The returned `dataTxId` field on the created `file` entity will be the endpoint that the manifest can be found on Arweave, just as explained in the [manifest sections](#uploading-manifests) above:
+
+```shell
+https://arweave.net/{dataTxId}
+https://arweave.net/{dataTxId}/custom-file-1
+https://arweave.net/{dataTxId}/custom-file-2
+```
+
## Other Utility Operations
### Monitoring Transactions
@@ -1220,3 +1269,4 @@ ardrive --help
[kb-wallets]: https://ardrive.atlassian.net/l/c/FpK8FuoQ
[arweave-manifests]: https://github.com/ArweaveTeam/arweave/wiki/Path-Manifests
[example-manifest-webpage]: https://arweave.net/qozq9YIUPEHfZhoTp9DkBpJuA_KNULBnfLiMroj5pZI
+[mozilla-mime-types]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
diff --git a/bats_test/constants.sh b/bats_test/constants.sh
new file mode 100644
index 00000000..064e91f2
--- /dev/null
+++ b/bats_test/constants.sh
@@ -0,0 +1,2 @@
+# String with 256 characters
+ENTITY_NAME_LONG='+==============================================================================================================================================================================================================================================================='
diff --git a/bats_test/create-drive/drive-name-validation.bats b/bats_test/create-drive/drive-name-validation.bats
new file mode 100644
index 00000000..9b3d7233
--- /dev/null
+++ b/bats_test/create-drive/drive-name-validation.bats
@@ -0,0 +1,20 @@
+#!/usr/bin/env bats
+
+# Support lib
+load '/home/node/packages/node_modules/bats-support/load.bash'
+# Assertions
+load '/home/node/packages/node_modules/bats-assert/load.bash'
+# Constants
+load '../constants.sh'
+
+@test "create drive with empty name results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive create-drive --dry-run -n '' -w $WALLET"
+
+ assert_line -n 0 "Error: Required parameter driveName wasn't provided!"
+}
+
+@test "create drive with long name results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive create-drive --dry-run -n $ENTITY_NAME_LONG -w $WALLET"
+
+ assert_line -n 0 'Error: The drive name must not exceed 255 bytes'
+}
diff --git a/bats_test/generate-wallet/generate-wallet.bats b/bats_test/generate-wallet/generate-wallet.bats
new file mode 100644
index 00000000..95610e5b
--- /dev/null
+++ b/bats_test/generate-wallet/generate-wallet.bats
@@ -0,0 +1,33 @@
+#!/usr/bin/env bats
+
+# Support lib
+load '/home/node/packages/node_modules/bats-support/load.bash'
+# Assertions
+load '/home/node/packages/node_modules/bats-assert/load.bash'
+
+@test "generate-wallet creates canonical wallet" {
+
+ run -0 bash -c "yarn ardrive generate-wallet -s 'this is an example twelve word seed phrase that you could use' | jq .kty,.n,.e,.d,.p,.q,.dp,.dq,.qi,.kid"
+
+ assert_line -n 0 '"RSA"'
+ assert_line -n 1 '"jw8e2rH-iNRaLsoutq7UIVrcK0wxqQzG7wJ8ip0J6uNDXDKzFB9H0WwcHXYZyhE2faS73J47Tob5KtpTlBXt6lCOnl8Vp0Ud3Y025lDhxMevnSVkTBY8SIxSsAOwwUDPFIJE9W2f9ZZ4DZh5hWy7n0wYWi4APWiwD7tfvxX8xHUcOOvZRiTY-JOmN7pOnFvwzblX4Mpg1kON-b31VLxhyZKUYFhDuEXsxX8mHT_kpbUdHV24DMch44SbzNkRSYKJ3IqSW6fBZo5Zt9wzpFe7VhhRrfM8ZEOVYWdebLQfMc0lxt4zXQCHpmrUub8e13GGCB2kL3q38sR7XU_fa1PyiTsPtYjH0v4cdvkK58dfnTZDlhfSJjoyNYi_MVuhdtw5drECxxGJQItE6IdWvnKW8yAjJs0rAxQ8vVQhw9Ss6Kj4exM-szuKjcNpbbS_k8dK1j-CELPJ1bh2rThfc2NFCGvweTtn-oatJDObqymkqOV2939k8Xydeb0cC3CTt4QGnO5zW3yffupT3DorX0T2OhTDHjVWVmXE0e5hACpguHTIMLF418t5OcH981EG2d0sTDDrCBVTWyhmADZpvdTryj4r2c4YgaPw8YElyGCGkhw3txAfMPAUPxtbx3v3OBeFCpSpIjXdRqrDapaDd3CLSBgHy3OuNF4_YBXSBIBiNpU"'
+ assert_line -n 2 '"AQAB"'
+ assert_line -n 3 '"W9efPVOsT3fU9dkDKHEQ6uEEHB_sedUkGemEvxCWf1-rrRM3eKGkX8SCQD_DysBGNV9-4-IJvR5V9Lb5lUaG2TkidnYg1qQ-yi-QoUgnyUdRbRfGTjqwNPsxUqLr5QWQbGA9mTrpyKbzJ_dNfOUThu85axvBN6tv7ImkrG6XOiDdH4X6lVeum9dejMRlF2jHLavhyQTkKmpwSXc0e1P9i4U5EKlZwIHTwe8hLwIH7oJZ0LKMKFfnX8OQqnBo8sKFczrbP1Bxjz-wRaNu3AEe2eT--yf_C3d__Wp5alww8q37pLKeUwS2EZbgPud-C15sZ-VvtLOaSJbvhDZ0_tiECa2KLFfI-riMS-Un6GjHPjpzts24IYARr9HtdlC6Z8wNBAzTIOYB0uPZ-kZQc2ieu-OHchIMelXds82f2wJYrs1zVKcpf7UQsb5Df5pgAh0biOQX-sto-HlIjixC2HuyDhl9j2irwRwauBJcM0Wookvd4i0CzRFMBF9LelVGZ_W036Wir-63CnslHEDKvNVsuGjyKFDeKBpD2s_XARN41h-TsSPj3N6hLoOerxiPbDYttPpbnfJ6SfkF174mNSt9x6LRP67mZIFHy9Lik9TsB6Tw09qrbd56obJDKUinFQaee0ephM7wBRH_KapikcYZ9vNPcGAI1AktYHRYifuqwsE"'
+ assert_line -n 4 '"8e4ylbMCNEz-p3Jc8j8exSty0MTpKQSJN8jv8QCVZgbyKogRPrxJBXz7P56WIwOsH_JghsfGeUJcsX-zyq-4xm5xwhT5ngd09Wr6Oj51Qjv4r8C2LCqFc4e7-Njt3hhgQLTQ2wMnBJVjbNStVLrpriL3dPm_d9-viPn3oqiJLdzS_TWmD7_1n0U9II2QaqhWjnEJ3DN_LkO5yju2w0IqwaRVfFTkpHv7EvS7D-Y83cAI3zBW-CkU6o04KEcdKP4SsmS0Rxuww4LuEp_f4XV84Z5mUo1m3umDeZccKRF03m7J2dQSRb58Nh0yOIWooIKDjCqVMuOoRejolVvkqcxraQ"'
+ assert_line -n 5 '"l2DzDQq_q1t42GfYL9llcsxdoas4wg9vCQwcEY8S_8Np7L2hJC7JzjSuR37z1_cJN4oSxcgtMoGy_LwTgYb-kCEzFAM9xH_Oa_Pgg4xfYtdD4Yzc-S9oZh_N6p2sub3MWaXW2UMFnwHolkUW3T_a9V7wmtv3IchB2pHmoNosYG3H-uOZgECmbLs39LbBvub9CHPtEo2FTfG5bnNuQootziTvWZGD40fJEKxOB4c3xzPT_xwtF2t_Z1tD76C3E516y2eIDAG_fTa5Bq6obe7-d4bAtfr5pgcWuU9jB1mTxxpBz94gFWB3kn-fPzbqirJZWo2E0dB_WqNjt3WcfAGoTQ"'
+ assert_line -n 6 '"kq-SDCIVXBCy9mPo5xhOV84YN2ys0inl6OT0VnO8IbhkbFVD333Z6HH3BIPrFB_N5TDYReAq_qq1-Qkswd_5cJbWco61KpPq3kKWpWnpPteN2UJHMlA1ye6qkh81Wkv9UD5Rw_kNV0Icnof08ELEHMkmsM4cwVnm3G5zLzRwuFYDt3Mn1LTXAFLC1VIAFisrEAKJr_GpUyvNIklCbvFXa0Fwc4old3WUrdGk-ebnUKx2tJxinuSJwg0N154GmCw0ueVFSTgI3QItxy0YWWMa7NFVyQfjL5T2Gmr8sAndimAyEtj6mz77oPPi11JzA58ek4XeIJTYGks1ehnVcv52wQ"'
+ assert_line -n 7 '"B_WrEgZ8qY_3vLlJHsr5kJ26VqPgKiQRnJIsb6fsQdKOoUofTP3A0rzmZRAB1ltA-tChyBCMf9leBfgFtovYms-EFgtNliV6PyblqUUaRuI2yYdUny2k-am2dB2yVVnrgtM7htUFWKULC8u6NgwDgV9qm1nxlq8m900wqUiPiMp89248RgggOBtoB9AMQ_N1Xppp3s3eMokBTRN4urr1SZ-bjkQegTbnFZ9Zwv-TKUBKZ2Gd5VtbqZ2c5t62gauIJ2XcO3VkEjtsYzOP26fzbWmCfI8jzV0WDsxj0qEdKhzxVPLDERyvbN6VCwfiMUTLef1y_G1QJP3uYlkmz0ZvCQ"'
+ assert_line -n 8 '"TgjJGFXrggHszRCRaccjeFJOHP22RMakqddAgImnMtStAFwOKdlNA1WND0xd4e1zVJso_IFRO9-kMrMv8JmLj-3QloS5-UFDlvfywfqHwPiyy3KVQtgGnm6PJ6WR2qbA2dcBgiGpM-lbC-t-mRa4YeNANmrTchr5RIlXNg4pVaFZUjG6-QDb4WL_CFmA5KgC70HFzaXFHvKFM2SYRo_lUytjINzsv6oayB6nFfhq0e1EyouyOY5cU5B57Lu9QSfMWdnbNdwW7pHNkZwOd9BqrRhBcO1JncHKsyr8bplWO1ffTftpMmSseDzaSd_Zi1n2h8dblu8jMh6pNAiWF46bBA"'
+ assert_line -n 9 '"2011-04-29"'
+}
+
+@test "generate-wallet rejects short and long seed phrases" {
+
+ run -1 bash -c "yarn ardrive generate-wallet -s 'invalid seed phrase'"
+
+ assert_line -n 0 "Error: 'invalid seed phrase' is not a valid 12 word seed phrase!"
+
+ run -1 bash -c "yarn ardrive generate-wallet -s 'this invalid seed phrase has thirteen words and is expected to be rejected'"
+
+ assert_line -n 0 "Error: 'this invalid seed phrase has thirteen words and is expected to be rejected' is not a valid 12 word seed phrase!"
+}
diff --git a/bats_test/get-address/get-address.bats b/bats_test/get-address/get-address.bats
new file mode 100644
index 00000000..a0164014
--- /dev/null
+++ b/bats_test/get-address/get-address.bats
@@ -0,0 +1,46 @@
+#!/usr/bin/env bats
+
+# Support lib
+load '/home/node/packages/node_modules/bats-support/load.bash'
+# Assertions
+load '/home/node/packages/node_modules/bats-assert/load.bash'
+
+@test "get-address finds the correct address of a canonical seed phrase" {
+
+ run -0 bash -c "yarn ardrive get-address -s 'this is an example twelve word seed phrase that you could use'"
+
+ assert_line -n 0 'HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k'
+}
+
+@test "get-address rejects short and long seed phrases" {
+
+ run -1 bash -c "yarn ardrive get-address -s 'invalid seed phrase'"
+
+ assert_line -n 0 "Error: 'invalid seed phrase' is not a valid 12 word seed phrase!"
+
+ run -1 bash -c "yarn ardrive get-address -s 'this invalid seed phrase has thirteen words and is expected to be rejected'"
+
+ assert_line -n 0 "Error: 'this invalid seed phrase has thirteen words and is expected to be rejected' is not a valid 12 word seed phrase!"
+}
+
+@test "get-address finds the correct address of a canonical wallet" {
+
+ WALLET='
+ {
+ "kty": "RSA",
+ "n": "jw8e2rH-iNRaLsoutq7UIVrcK0wxqQzG7wJ8ip0J6uNDXDKzFB9H0WwcHXYZyhE2faS73J47Tob5KtpTlBXt6lCOnl8Vp0Ud3Y025lDhxMevnSVkTBY8SIxSsAOwwUDPFIJE9W2f9ZZ4DZh5hWy7n0wYWi4APWiwD7tfvxX8xHUcOOvZRiTY-JOmN7pOnFvwzblX4Mpg1kON-b31VLxhyZKUYFhDuEXsxX8mHT_kpbUdHV24DMch44SbzNkRSYKJ3IqSW6fBZo5Zt9wzpFe7VhhRrfM8ZEOVYWdebLQfMc0lxt4zXQCHpmrUub8e13GGCB2kL3q38sR7XU_fa1PyiTsPtYjH0v4cdvkK58dfnTZDlhfSJjoyNYi_MVuhdtw5drECxxGJQItE6IdWvnKW8yAjJs0rAxQ8vVQhw9Ss6Kj4exM-szuKjcNpbbS_k8dK1j-CELPJ1bh2rThfc2NFCGvweTtn-oatJDObqymkqOV2939k8Xydeb0cC3CTt4QGnO5zW3yffupT3DorX0T2OhTDHjVWVmXE0e5hACpguHTIMLF418t5OcH981EG2d0sTDDrCBVTWyhmADZpvdTryj4r2c4YgaPw8YElyGCGkhw3txAfMPAUPxtbx3v3OBeFCpSpIjXdRqrDapaDd3CLSBgHy3OuNF4_YBXSBIBiNpU",
+ "e": "AQAB",
+ "d": "W9efPVOsT3fU9dkDKHEQ6uEEHB_sedUkGemEvxCWf1-rrRM3eKGkX8SCQD_DysBGNV9-4-IJvR5V9Lb5lUaG2TkidnYg1qQ-yi-QoUgnyUdRbRfGTjqwNPsxUqLr5QWQbGA9mTrpyKbzJ_dNfOUThu85axvBN6tv7ImkrG6XOiDdH4X6lVeum9dejMRlF2jHLavhyQTkKmpwSXc0e1P9i4U5EKlZwIHTwe8hLwIH7oJZ0LKMKFfnX8OQqnBo8sKFczrbP1Bxjz-wRaNu3AEe2eT--yf_C3d__Wp5alww8q37pLKeUwS2EZbgPud-C15sZ-VvtLOaSJbvhDZ0_tiECa2KLFfI-riMS-Un6GjHPjpzts24IYARr9HtdlC6Z8wNBAzTIOYB0uPZ-kZQc2ieu-OHchIMelXds82f2wJYrs1zVKcpf7UQsb5Df5pgAh0biOQX-sto-HlIjixC2HuyDhl9j2irwRwauBJcM0Wookvd4i0CzRFMBF9LelVGZ_W036Wir-63CnslHEDKvNVsuGjyKFDeKBpD2s_XARN41h-TsSPj3N6hLoOerxiPbDYttPpbnfJ6SfkF174mNSt9x6LRP67mZIFHy9Lik9TsB6Tw09qrbd56obJDKUinFQaee0ephM7wBRH_KapikcYZ9vNPcGAI1AktYHRYifuqwsE",
+ "p": "8e4ylbMCNEz-p3Jc8j8exSty0MTpKQSJN8jv8QCVZgbyKogRPrxJBXz7P56WIwOsH_JghsfGeUJcsX-zyq-4xm5xwhT5ngd09Wr6Oj51Qjv4r8C2LCqFc4e7-Njt3hhgQLTQ2wMnBJVjbNStVLrpriL3dPm_d9-viPn3oqiJLdzS_TWmD7_1n0U9II2QaqhWjnEJ3DN_LkO5yju2w0IqwaRVfFTkpHv7EvS7D-Y83cAI3zBW-CkU6o04KEcdKP4SsmS0Rxuww4LuEp_f4XV84Z5mUo1m3umDeZccKRF03m7J2dQSRb58Nh0yOIWooIKDjCqVMuOoRejolVvkqcxraQ",
+ "q": "l2DzDQq_q1t42GfYL9llcsxdoas4wg9vCQwcEY8S_8Np7L2hJC7JzjSuR37z1_cJN4oSxcgtMoGy_LwTgYb-kCEzFAM9xH_Oa_Pgg4xfYtdD4Yzc-S9oZh_N6p2sub3MWaXW2UMFnwHolkUW3T_a9V7wmtv3IchB2pHmoNosYG3H-uOZgECmbLs39LbBvub9CHPtEo2FTfG5bnNuQootziTvWZGD40fJEKxOB4c3xzPT_xwtF2t_Z1tD76C3E516y2eIDAG_fTa5Bq6obe7-d4bAtfr5pgcWuU9jB1mTxxpBz94gFWB3kn-fPzbqirJZWo2E0dB_WqNjt3WcfAGoTQ",
+ "dp": "kq-SDCIVXBCy9mPo5xhOV84YN2ys0inl6OT0VnO8IbhkbFVD333Z6HH3BIPrFB_N5TDYReAq_qq1-Qkswd_5cJbWco61KpPq3kKWpWnpPteN2UJHMlA1ye6qkh81Wkv9UD5Rw_kNV0Icnof08ELEHMkmsM4cwVnm3G5zLzRwuFYDt3Mn1LTXAFLC1VIAFisrEAKJr_GpUyvNIklCbvFXa0Fwc4old3WUrdGk-ebnUKx2tJxinuSJwg0N154GmCw0ueVFSTgI3QItxy0YWWMa7NFVyQfjL5T2Gmr8sAndimAyEtj6mz77oPPi11JzA58ek4XeIJTYGks1ehnVcv52wQ",
+ "dq": "B_WrEgZ8qY_3vLlJHsr5kJ26VqPgKiQRnJIsb6fsQdKOoUofTP3A0rzmZRAB1ltA-tChyBCMf9leBfgFtovYms-EFgtNliV6PyblqUUaRuI2yYdUny2k-am2dB2yVVnrgtM7htUFWKULC8u6NgwDgV9qm1nxlq8m900wqUiPiMp89248RgggOBtoB9AMQ_N1Xppp3s3eMokBTRN4urr1SZ-bjkQegTbnFZ9Zwv-TKUBKZ2Gd5VtbqZ2c5t62gauIJ2XcO3VkEjtsYzOP26fzbWmCfI8jzV0WDsxj0qEdKhzxVPLDERyvbN6VCwfiMUTLef1y_G1QJP3uYlkmz0ZvCQ",
+ "qi": "TgjJGFXrggHszRCRaccjeFJOHP22RMakqddAgImnMtStAFwOKdlNA1WND0xd4e1zVJso_IFRO9-kMrMv8JmLj-3QloS5-UFDlvfywfqHwPiyy3KVQtgGnm6PJ6WR2qbA2dcBgiGpM-lbC-t-mRa4YeNANmrTchr5RIlXNg4pVaFZUjG6-QDb4WL_CFmA5KgC70HFzaXFHvKFM2SYRo_lUytjINzsv6oayB6nFfhq0e1EyouyOY5cU5B57Lu9QSfMWdnbNdwW7pHNkZwOd9BqrRhBcO1JncHKsyr8bplWO1ffTftpMmSseDzaSd_Zi1n2h8dblu8jMh6pNAiWF46bBA",
+ "kid": "2011-04-29"
+ }
+ '
+
+ run -0 bash -c 'echo $WALLET | yarn ardrive get-address -w /dev/stdin'
+
+ assert_line -n 0 'HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k'
+}
diff --git a/bats_test/rename/rename-drive/rename_invalid_names.sh b/bats_test/rename/rename-drive/rename_invalid_names.sh
index 55ae18b9..a26f23c2 100644
--- a/bats_test/rename/rename-drive/rename_invalid_names.sh
+++ b/bats_test/rename/rename-drive/rename_invalid_names.sh
@@ -1,10 +1,8 @@
#!/bin/bash
# Depends on ./rename_drive.sh
+# Depends on ../..constants.sh
rename_invalid_names() {
- echo "$(rename_drive "${PUB_DRIVE_ID}" " leading spaces.txt")"
- echo "$(rename_drive "${PUB_DRIVE_ID}" "trailing spaces.png ")"
- echo "$(rename_drive "${PUB_DRIVE_ID}" "trailing dots.doc.")"
- echo "$(rename_drive "${PUB_DRIVE_ID}" "reserved characters :*.txt")"
+ echo "$(rename_drive "${PUB_DRIVE_ID}" "$ENTITY_NAME_LONG")"
}
diff --git a/bats_test/rename/rename-drive/rename_public_drive.bats b/bats_test/rename/rename-drive/rename_public_drive.bats
index 2b5a0d1e..b215043c 100644
--- a/bats_test/rename/rename-drive/rename_public_drive.bats
+++ b/bats_test/rename/rename-drive/rename_public_drive.bats
@@ -13,6 +13,8 @@ load './rename_drive.sh'
load './rename_invalid_names.sh'
# A function which triggers a rename with exactly the same name. DEPENDS on rename_drive.sh
load './rename_same_name.sh'
+# Constants
+load '../../constants.sh'
DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" >/dev/null 2>&1 && pwd)"
@@ -26,10 +28,7 @@ setup_file() {
# Note 1: The above function runs the command with multiple invalid examples, so we get multiple lines logged in this case
# Note 2: We don't assert for the exit code (a.k.a. "${status}") here because of `Note 1`, so we assert the error by reading the output
- assert_line -n 0 "Error: The drive name cannot start with spaces"
- assert_line -n 1 "Error: The drive name cannot have trailing dots or spaces"
- assert_line -n 2 "Error: The drive name cannot have trailing dots or spaces"
- assert_line -n 3 "Error: The drive name cannot contain reserved characters (i.e. '\\\\', '/', ':', '*', '?', '\"', '<', '>', '|')"
+ assert_line -n 0 'Error: The drive name must not exceed 255 bytes'
}
@test 'Errors out if renaming would end up having no effect' {
diff --git a/bats_test/rename/rename-file/rename_invalid_names.sh b/bats_test/rename/rename-file/rename_invalid_names.sh
index 9100644a..9b2fc81a 100644
--- a/bats_test/rename/rename-file/rename_invalid_names.sh
+++ b/bats_test/rename/rename-file/rename_invalid_names.sh
@@ -1,10 +1,8 @@
#!/bin/bash
# Depends on ./rename_file.sh
+# Depends on ../..constants.sh
rename_invalid_names() {
- echo "$(rename_file "${PUB_FILE_ID}" " leading spaces.txt")"
- echo "$(rename_file "${PUB_FILE_ID}" "trailing spaces.png ")"
- echo "$(rename_file "${PUB_FILE_ID}" "trailing dots.doc.")"
- echo "$(rename_file "${PUB_FILE_ID}" "reserved characters :*.txt")"
+ echo "$(rename_file "${PUB_FILE_ID}" "$ENTITY_NAME_LONG")"
}
diff --git a/bats_test/rename/rename-file/rename_public_file.bats b/bats_test/rename/rename-file/rename_public_file.bats
index cc5374c9..7a801c8f 100644
--- a/bats_test/rename/rename-file/rename_public_file.bats
+++ b/bats_test/rename/rename-file/rename_public_file.bats
@@ -15,6 +15,8 @@ load './rename_invalid_names.sh'
load './rename_colliding_name.sh'
# A function which triggers a rename with exactly the same name. DEPENDS on rename_file.sh
load './rename_same_name.sh'
+# Constants
+load '../../constants.sh'
DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" >/dev/null 2>&1 && pwd)"
@@ -28,10 +30,7 @@ setup_file() {
# Note 1: The above function runs the command with multiple invalid examples, so we get multiple lines logged in this case
# Note 2: We don't assert for the exit code (a.k.a. "${status}") here because of `Note 1`, so we assert the error by reading the output
- assert_line -n 0 "Error: The file name cannot start with spaces"
- assert_line -n 1 "Error: The file name cannot have trailing dots or spaces"
- assert_line -n 2 "Error: The file name cannot have trailing dots or spaces"
- assert_line -n 3 "Error: The file name cannot contain reserved characters (i.e. '\\\\', '/', ':', '*', '?', '\"', '<', '>', '|')"
+ assert_line -n 0 'Error: The file name must not exceed 255 bytes'
}
@test 'Errors out if the rename would end up in a name collision' {
diff --git a/bats_test/rename/rename-folder/rename_invalid_names.sh b/bats_test/rename/rename-folder/rename_invalid_names.sh
index 5d9c7107..e4f1f744 100644
--- a/bats_test/rename/rename-folder/rename_invalid_names.sh
+++ b/bats_test/rename/rename-folder/rename_invalid_names.sh
@@ -1,10 +1,8 @@
#!/bin/bash
# Depends on ./rename_folder.sh
+# Depends on ../..constants.sh
rename_invalid_names() {
- echo "$(rename_folder "${FOLDER_ID}" " leading spaces.txt")"
- echo "$(rename_folder "${FOLDER_ID}" "trailing spaces.png ")"
- echo "$(rename_folder "${FOLDER_ID}" "trailing dots.doc.")"
- echo "$(rename_folder "${FOLDER_ID}" "reserved characters :*.txt")"
+ echo "$(rename_folder "${FOLDER_ID}" "$ENTITY_NAME_LONG")"
}
diff --git a/bats_test/rename/rename-folder/rename_public_folder.bats b/bats_test/rename/rename-folder/rename_public_folder.bats
index c78f92be..03ea98c4 100644
--- a/bats_test/rename/rename-folder/rename_public_folder.bats
+++ b/bats_test/rename/rename-folder/rename_public_folder.bats
@@ -15,6 +15,9 @@ load './rename_invalid_names.sh'
load './rename_colliding_name.sh'
# A function which triggers a rename with exactly the same name. DEPENDS on rename_folder.sh
load './rename_same_name.sh'
+# Constants
+load '../../constants.sh'
+
DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" >/dev/null 2>&1 && pwd)"
@@ -28,10 +31,7 @@ setup_file() {
# Note 1: The above function runs the command with multiple invalid examples, so we get multiple lines logged in this case
# Note 2: We don't assert for the exit code (a.k.a. "${status}") here because of `Note 1`, so we assert the error by reading the output
- assert_line -n 0 "Error: The folder name cannot start with spaces"
- assert_line -n 1 "Error: The folder name cannot have trailing dots or spaces"
- assert_line -n 2 "Error: The folder name cannot have trailing dots or spaces"
- assert_line -n 3 "Error: The folder name cannot contain reserved characters (i.e. '\\\\', '/', ':', '*', '?', '\"', '<', '>', '|')"
+ assert_line -n 0 'Error: The folder name must not exceed 255 bytes'
}
@test 'Errors out if the rename would end up in a name collision' {
diff --git a/bats_test/upload-file/create-file.sh b/bats_test/upload-file/create-file.sh
index 68a676c2..68f3d3ad 100755
--- a/bats_test/upload-file/create-file.sh
+++ b/bats_test/upload-file/create-file.sh
@@ -1,5 +1,6 @@
#!/bin/bash
-yes "10 chunk test" | head --bytes 2621440 > /home/node/10Chunks.txt
+yes "10 chunk test" | head -c 2621440 > /home/node/10Chunks.txt
+yes "1 chunk test" | head -c 262144 > /home/node/1Chunk.txt
exit 0
diff --git a/bats_test/upload-file/create-folder.sh b/bats_test/upload-file/create-folder.sh
new file mode 100755
index 00000000..32c30c64
--- /dev/null
+++ b/bats_test/upload-file/create-folder.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+cd /home/node
+
+mkdir root_folder
+cd root_folder
+yes "1 chunk test" | head -c 262144 > 1Chunk.txt
+
+mkdir parent_folder
+cd parent_folder
+yes "5 chunk test" | head -c 1310720 > 5Chunk.txt
+
+mkdir child_folder
+cd child_folder
+yes "20 chunk test" | head -c 5242880 > 20Chunk.txt
+
+exit 0
diff --git a/bats_test/upload-file/file-name-validation.bats b/bats_test/upload-file/file-name-validation.bats
new file mode 100644
index 00000000..c09c2560
--- /dev/null
+++ b/bats_test/upload-file/file-name-validation.bats
@@ -0,0 +1,43 @@
+#!/usr/bin/env bats
+
+# Support lib
+load '/home/node/packages/node_modules/bats-support/load.bash'
+# Assertions
+load '/home/node/packages/node_modules/bats-assert/load.bash'
+# Constants
+load '../constants.sh'
+
+DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" >/dev/null 2>&1 && pwd)"
+
+setup_file() {
+ run $DIR/create-file.sh
+ echo "##### Created 10 chunk test file" >>/home/node/bats.log
+
+ # Start test from correct working directory
+ # TODO: Use a $HOME variable for local testing support
+ cd /home/node/ardrive-cli
+}
+
+@test "upload file with empty destination file name results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET -d ''"
+
+ assert_line -n 0 "Error: The file name cannot be empty"
+}
+
+@test "upload file with empty destination file name (no bundle) results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET -d '' --no-bundle"
+
+ assert_line -n 0 "Error: The file name cannot be empty"
+}
+
+@test "upload file with long destination file name results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET -d $ENTITY_NAME_LONG"
+
+ assert_line -n 0 'Error: The file name must not exceed 255 bytes'
+}
+
+@test "upload file with long destination file name (no bundle) results in an error message and exit code 1" {
+ run -1 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET --no-bundle -d $ENTITY_NAME_LONG"
+
+ assert_line -n 0 'Error: The file name must not exceed 255 bytes'
+}
diff --git a/bats_test/upload-file/single-bundled-files.bats b/bats_test/upload-file/single-bundled-files.bats
index f5e83d0f..bacc1300 100644
--- a/bats_test/upload-file/single-bundled-files.bats
+++ b/bats_test/upload-file/single-bundled-files.bats
@@ -5,15 +5,21 @@ load '/home/node/packages/node_modules/bats-support/load.bash'
# Assertions
load '/home/node/packages/node_modules/bats-assert/load.bash'
+DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")" >/dev/null 2>&1 && pwd)"
+
setup_file() {
run $DIR/create-file.sh
- echo "##### Created 10 chunk test file" >>/home/node/bats.log
+ echo "##### Created test files" >>/home/node/bats.log
+
+ run $DIR/create-folder.sh
+ echo "##### Created bulk test folder" >>/home/node/bats.log
# Start test from correct working directory
# TODO: Use a $HOME variable for local testing support
cd /home/node/ardrive-cli
}
+
@test "upload-file creates a bundled transaction by default" {
run -0 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
@@ -30,18 +36,49 @@ setup_file() {
assert_line -n 1 ''
}
-@test "upload-file creates 2 bundle transactions with 2 files and --local-paths" {
- run -0 bash -c "yarn ardrive upload-file --dry-run --local-paths '/home/node/10Chunks.txt' '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
+@test "upload-file throws an error if multiple files are sent up with the same name" {
+ run bash -c "yarn ardrive upload-file --dry-run --local-paths '/home/node/1Chunk.txt' '/home/node/1Chunk.txt' -F $PUB_FOLD_ID -w $WALLET"
+
+ assert_output "Error: Upload cannot contain multiple destination names to the same destination folder!"
+}
+
+@test "upload-file creates 1 bundled transaction with 2 files and --local-paths" {
+ run -0 bash -c "yarn ardrive upload-file --dry-run --local-paths '/home/node/10Chunks.txt' '/home/node/1Chunk.txt' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
assert_line -n 0 '"file"'
- assert_line -n 1 '"bundle"'
- assert_line -n 2 '"file"'
- assert_line -n 3 '"bundle"'
- assert_line -n 4 ''
+ assert_line -n 1 '"file"'
+ assert_line -n 2 '"bundle"'
+ assert_line -n 3 ''
+
+}
+
+@test "upload-file used on a bulk folder returns the expected results" {
+ run -0 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/root_folder' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
+
+ assert_line -n 0 '"folder"'
+ assert_line -n 1 '"folder"'
+ assert_line -n 2 '"folder"'
+ assert_line -n 3 '"file"'
+ assert_line -n 4 '"file"'
+ assert_line -n 5 '"file"'
+ assert_line -n 6 '"bundle"'
+ assert_line -n 7 ''
+}
+
+@test "upload-file used on a bulk folder with --no-bundle returns the expected results" {
+ run -0 bash -c "yarn ardrive upload-file --no-bundle --dry-run --local-path '/home/node/root_folder' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
+
+ assert_line -n 0 '"folder"'
+ assert_line -n 1 '"folder"'
+ assert_line -n 2 '"folder"'
+ assert_line -n 3 '"file"'
+ assert_line -n 4 '"file"'
+ assert_line -n 5 '"file"'
+ assert_line -n 7 ''
}
@test "upload-file creates v2 transactions with 2 files --local-paths and --no-bundle" {
- run -0 bash -c "yarn ardrive upload-file --no-bundle --dry-run --local-paths '/home/node/10Chunks.txt' '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
+ run -0 bash -c "yarn ardrive upload-file --no-bundle --dry-run --local-paths '/home/node/10Chunks.txt' '/home/node/1Chunk.txt' -F $PUB_FOLD_ID -w $WALLET | jq '.created[] .type'"
assert_line -n 0 '"file"'
assert_line -n 1 '"file"'
@@ -52,6 +89,14 @@ setup_file() {
run -0 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/10Chunks.txt' -F $PUB_FOLD_ID -w $WALLET | jq -r '.fees | keys | .[]'"
assert_line -n 0 --regexp '^(\w|-){43}$'
+ assert_line -n 1 ''
+}
+
+@test "upload bulk folder bundled produces one fee" {
+ run -0 bash -c "yarn ardrive upload-file --dry-run --local-path '/home/node/root_folder' -F $PUB_FOLD_ID -w $WALLET | jq -r '.fees | keys | .[]'"
+
+ assert_line -n 0 --regexp '^(\w|-){43}$'
+ assert_line -n 1 ''
}
@test "upload file as v2 transactions produces two fees" {
@@ -59,6 +104,22 @@ setup_file() {
assert_line -n 0 --regexp '^(\w|-){43}$'
assert_line -n 1 --regexp '^(\w|-){43}$'
+ assert_line -n 2 ''
+}
+
+@test "upload bulk folder as v2 tx produces nine fees" {
+ run -0 bash -c "yarn ardrive upload-file --no-bundle --dry-run --local-path '/home/node/root_folder' -F $PUB_FOLD_ID -w $WALLET | jq -r '.fees | keys | .[]'"
+
+ assert_line -n 0 --regexp '^(\w|-){43}$'
+ assert_line -n 1 --regexp '^(\w|-){43}$'
+ assert_line -n 2 --regexp '^(\w|-){43}$'
+ assert_line -n 3 --regexp '^(\w|-){43}$'
+ assert_line -n 4 --regexp '^(\w|-){43}$'
+ assert_line -n 5 --regexp '^(\w|-){43}$'
+ assert_line -n 6 --regexp '^(\w|-){43}$'
+ assert_line -n 7 --regexp '^(\w|-){43}$'
+ assert_line -n 8 --regexp '^(\w|-){43}$'
+ assert_line -n 9 ''
}
@test "Duplicate name uploads nothing with --skip" {
diff --git a/package.json b/package.json
index 231bb4e3..f6838b7c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ardrive-cli",
- "version": "1.8.0",
+ "version": "1.9.0",
"description": "The ArDrive Command Line Interface (CLI is a Node.js application for terminal-based ArDrive workflows. It also offers utility operations for securely interacting with Arweave wallets and inspecting various Arweave blockchain conditions.",
"main": "./lib/index.js",
"bin": {
@@ -8,7 +8,7 @@
},
"types": "./lib/index.d.ts",
"dependencies": {
- "ardrive-core-js": "1.7.0",
+ "ardrive-core-js": "1.8.0",
"arweave": "1.10.18",
"axios": "^0.21.1",
"commander": "^8.2.0",
diff --git a/src/CLICommand/parameters_helper.test.ts b/src/CLICommand/parameters_helper.test.ts
index 5ecb36be..47227e60 100644
--- a/src/CLICommand/parameters_helper.test.ts
+++ b/src/CLICommand/parameters_helper.test.ts
@@ -246,7 +246,7 @@ describe('ParametersHelper class', () => {
it('returns a wallet when a valid --seed-phrase option is provided', function () {
// FIXME: it takes too long
- this.timeout(60_000);
+ this.timeout(120_000);
const cmd = declareCommandWithParams(program, [SeedPhraseParameter]);
CLICommand.parse(program, [
...baseArgv,
diff --git a/src/commands/generate_wallet.ts b/src/commands/generate_wallet.ts
index fa2529db..73949c50 100644
--- a/src/commands/generate_wallet.ts
+++ b/src/commands/generate_wallet.ts
@@ -12,7 +12,7 @@ new CLICommand({
const parameters = new ParametersHelper(options);
const seedPhrase = parameters.getRequiredParameterValue(SeedPhraseParameter);
const wallet = await cliWalletDao.generateJWKWallet(new SeedPhrase(seedPhrase));
- console.log(JSON.stringify(wallet));
+ console.log(JSON.stringify(wallet['jwk'] || wallet));
return SUCCESS_EXIT_CODE;
})
});
diff --git a/src/commands/upload_file.ts b/src/commands/upload_file.ts
index 40fe55cf..f28550fb 100644
--- a/src/commands/upload_file.ts
+++ b/src/commands/upload_file.ts
@@ -12,9 +12,10 @@ import {
ParentFolderIdParameter,
WalletFileParameter,
LocalPathParameter,
- LocalCSVParameter
+ LocalCSVParameter,
+ CustomContentTypeParameter
} from '../parameter_declarations';
-import { fileUploadConflictPrompts, folderUploadConflictPrompts } from '../prompts';
+import { fileAndFolderUploadConflictPrompts } from '../prompts';
import { ERROR_EXIT_CODE, SUCCESS_EXIT_CODE } from '../CLICommand/error_codes';
import { CLIAction } from '../CLICommand/action';
import {
@@ -26,7 +27,7 @@ import {
wrapFileOrFolder,
EID,
readJWKFile,
- isFolder
+ ArDriveUploadStats
} from 'ardrive-core-js';
import { cliArDriveFactory } from '..';
import * as fs from 'fs';
@@ -54,10 +55,15 @@ function getFilesFromCSV(parameters: ParametersHelper): UploadPathParameter[] |
const csvRows = localCSVFileData.split(ROW_SEPARATOR);
const fileParameters: UploadPathParameter[] = csvRows.map((row: string) => {
const csvFields = row.split(COLUMN_SEPARATOR).map((f: string) => f.trim());
- const [localFilePath, destinationFileName, _parentFolderId, drivePassword, _driveKey] = csvFields;
+ // eslint-disable-next-line prettier/prettier
+ const [localFilePath, destinationFileName, _parentFolderId, drivePassword, _driveKey, _customContentType] =
+ // eslint-disable-next-line prettier/prettier
+ csvFields;
+
+ const customContentType = _customContentType ?? parameters.getParameterValue(CustomContentTypeParameter);
// TODO: Make CSV uploads more bulk performant
- const wrappedEntity = wrapFileOrFolder(localFilePath);
+ const wrappedEntity = wrapFileOrFolder(localFilePath, customContentType);
const parentFolderId = EID(
_parentFolderId ? _parentFolderId : parameters.getRequiredParameterValue(ParentFolderIdParameter)
);
@@ -80,9 +86,10 @@ function getFileList(parameters: ParametersHelper, parentFolderId: FolderID): Up
if (!localPaths) {
return undefined;
}
+ const customContentType = parameters.getParameterValue(CustomContentTypeParameter);
const localPathsToUpload = localPaths.map((filePath: FilePath) => {
- const wrappedEntity = wrapFileOrFolder(filePath);
+ const wrappedEntity = wrapFileOrFolder(filePath, customContentType);
return {
parentFolderId,
@@ -96,11 +103,15 @@ function getFileList(parameters: ParametersHelper, parentFolderId: FolderID): Up
function getSingleFile(parameters: ParametersHelper, parentFolderId: FolderID): UploadPathParameter[] {
// NOTE: Single file is the last possible use case. Throw exception if the parameter isn't found.
const localFilePath =
- parameters.getParameterValue(LocalFilePathParameter_DEPRECATED, wrapFileOrFolder) ??
- parameters.getRequiredParameterValue(LocalPathParameter, wrapFileOrFolder);
+ parameters.getParameterValue(LocalFilePathParameter_DEPRECATED) ??
+ parameters.getRequiredParameterValue(LocalPathParameter);
+
+ const customContentType = parameters.getParameterValue(CustomContentTypeParameter);
+
+ const wrappedEntity = wrapFileOrFolder(localFilePath, customContentType);
const singleParameter = {
parentFolderId: parentFolderId,
- wrappedEntity: localFilePath,
+ wrappedEntity,
destinationFileName: parameters.getParameterValue(DestinationFileNameParameter)
};
@@ -131,9 +142,9 @@ new CLICommand({
ShouldBundleParameter,
...ConflictResolutionParams,
...DrivePrivacyParameters,
+ CustomContentTypeParameter,
LocalFilePathParameter_DEPRECATED,
- LocalFilesParameter_DEPRECATED,
- BoostParameter
+ LocalFilesParameter_DEPRECATED
],
action: new CLIAction(async function action(options) {
const parameters = new ParametersHelper(options);
@@ -169,66 +180,34 @@ new CLICommand({
shouldBundle
});
- const results = await Promise.all(
- filesToUpload.map(async (fileToUpload) => {
- const {
- parentFolderId,
- wrappedEntity,
- destinationFileName,
- drivePassword,
- driveKey: fileDriveKey
- } = fileToUpload;
-
- return await (async () => {
- if (await parameters.getIsPrivate()) {
- const driveId = await arDrive.getDriveIdForFolderId(parentFolderId);
- const driveKey =
- fileDriveKey ??
- (await parameters.getDriveKey({ driveId, drivePassword, useCache: true }));
-
- if (isFolder(wrappedEntity)) {
- return arDrive.createPrivateFolderAndUploadChildren({
- parentFolderId,
- wrappedFolder: wrappedEntity,
- driveKey,
- destParentFolderName: destinationFileName,
- conflictResolution,
- prompts: folderUploadConflictPrompts
- });
- } else {
- return arDrive.uploadPrivateFile({
- parentFolderId,
- wrappedFile: wrappedEntity,
- driveKey,
- destinationFileName,
- conflictResolution,
- prompts: fileUploadConflictPrompts
- });
- }
- } else {
- if (isFolder(wrappedEntity)) {
- return arDrive.createPublicFolderAndUploadChildren({
- parentFolderId,
- wrappedFolder: wrappedEntity,
- destParentFolderName: destinationFileName,
- conflictResolution,
- prompts: folderUploadConflictPrompts
- });
- } else {
- return arDrive.uploadPublicFile({
- parentFolderId,
- wrappedFile: wrappedEntity,
- destinationFileName,
- conflictResolution,
- prompts: fileUploadConflictPrompts
- });
- }
- }
- })();
- })
+ const uploadStats: ArDriveUploadStats[] = await Promise.all(
+ filesToUpload.map(
+ async ({ parentFolderId, wrappedEntity, destinationFileName, driveKey, drivePassword }) => {
+ driveKey ??= (await parameters.getIsPrivate())
+ ? await parameters.getDriveKey({
+ driveId: await arDrive.getDriveIdForFolderId(parentFolderId),
+ drivePassword,
+ useCache: true
+ })
+ : undefined;
+
+ return {
+ wrappedEntity,
+ driveKey,
+ destFolderId: parentFolderId,
+ destName: destinationFileName
+ };
+ }
+ )
);
- console.log(JSON.stringify(formatResults(results), null, 4));
+ const results = await arDrive.uploadAllEntities({
+ entitiesToUpload: uploadStats,
+ conflictResolution,
+ prompts: fileAndFolderUploadConflictPrompts
+ });
+
+ console.log(JSON.stringify(results, null, 4));
return SUCCESS_EXIT_CODE;
}
diff --git a/src/parameter_declarations.ts b/src/parameter_declarations.ts
index d0354200..c7dc0afe 100644
--- a/src/parameter_declarations.ts
+++ b/src/parameter_declarations.ts
@@ -38,6 +38,7 @@ export const ShouldBundleParameter = 'bundle'; // commander maps --no-x style pa
export const LocalPathParameter = 'localPath';
export const LocalPathsParameter = 'localPaths';
export const LocalCSVParameter = 'localCsv';
+export const CustomContentTypeParameter = 'contentType';
// Aggregates for convenience
export const WalletTypeParameters = [WalletFileParameter, SeedPhraseParameter];
@@ -441,3 +442,10 @@ Parameter.declare({
DestinationFileNameParameter
]
});
+
+Parameter.declare({
+ name: CustomContentTypeParameter,
+ aliases: ['--content-type'],
+ description:
+ '(OPTIONAL) Provide a custom content type to all files within the upload to be used by the gateway to display the content'
+});
diff --git a/src/prompts.ts b/src/prompts.ts
index a41ba500..ea37a8c3 100644
--- a/src/prompts.ts
+++ b/src/prompts.ts
@@ -169,7 +169,7 @@ export const fileUploadConflictPrompts: FileConflictPrompts = {
fileToFolderNameConflict
};
-export const folderUploadConflictPrompts: FolderConflictPrompts = {
+export const fileAndFolderUploadConflictPrompts: FolderConflictPrompts = {
...fileUploadConflictPrompts,
folderToFolderNameConflict,
folderToFileNameConflict
diff --git a/yarn.lock b/yarn.lock
index b89edd64..c2d30f71 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1538,7 +1538,7 @@ __metadata:
"@types/source-map-support": ^0
"@typescript-eslint/eslint-plugin": ^4.18.0
"@typescript-eslint/parser": ^4.18.0
- ardrive-core-js: 1.7.0
+ ardrive-core-js: 1.8.0
arweave: 1.10.18
axios: ^0.21.1
chai: ^4.3.4
@@ -1564,9 +1564,9 @@ __metadata:
languageName: unknown
linkType: soft
-"ardrive-core-js@npm:1.7.0":
- version: 1.7.0
- resolution: "ardrive-core-js@npm:1.7.0"
+"ardrive-core-js@npm:1.8.0":
+ version: 1.8.0
+ resolution: "ardrive-core-js@npm:1.8.0"
dependencies:
"@alexsasharegan/simple-cache": ^3.3.3
arbundles: ^0.5.5
@@ -1584,7 +1584,7 @@ __metadata:
smartweave: ^0.4.45
utf8: ^3.0.0
uuid: ^8.3.2
- checksum: 3223964ec9ad96a984fdc71f86e37b049fa5d2f193c3eb78350268bf67c9e31d7713e01e4cfc28bad9a9dfdafd2d3a343a7b41bd37d860f294c83c16aae46257
+ checksum: 091a457022ecf780c693ad30a88ee3becf4c930884a72cb5f4b147b1568d1eba537a49947323fa3148fce10c7c79798f9637a1b2d13c631ae035a83fe4f1149b
languageName: node
linkType: hard