diff --git a/.pnp.js b/.pnp.js index 1fb4c851..8f61819a 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.0.4"], + ["ardrive-core-js", "npm:1.0.5"], ["arweave", "npm:1.10.16"], ["axios", "npm:0.21.1"], ["chai", "npm:4.3.4"], @@ -1293,7 +1293,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.0.4"], + ["ardrive-core-js", "npm:1.0.5"], ["arweave", "npm:1.10.16"], ["axios", "npm:0.21.1"], ["chai", "npm:4.3.4"], @@ -1319,10 +1319,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["ardrive-core-js", [ - ["npm:1.0.4", { - "packageLocation": "./.yarn/cache/ardrive-core-js-npm-1.0.4-8e5129121f-22109e31bc.zip/node_modules/ardrive-core-js/", + ["npm:1.0.5", { + "packageLocation": "./.yarn/cache/ardrive-core-js-npm-1.0.5-6e82770d54-02490de5ea.zip/node_modules/ardrive-core-js/", "packageDependencies": [ - ["ardrive-core-js", "npm:1.0.4"], + ["ardrive-core-js", "npm:1.0.5"], ["arweave", "npm:1.10.16"], ["arweave-bundles", "npm:1.0.3"], ["arweave-mnemonic-keys", "npm:0.0.9"], diff --git a/.yarn/cache/ardrive-core-js-npm-1.0.4-8e5129121f-22109e31bc.zip b/.yarn/cache/ardrive-core-js-npm-1.0.5-6e82770d54-02490de5ea.zip similarity index 75% rename from .yarn/cache/ardrive-core-js-npm-1.0.4-8e5129121f-22109e31bc.zip rename to .yarn/cache/ardrive-core-js-npm-1.0.5-6e82770d54-02490de5ea.zip index 234492cb..a3ae4a10 100644 Binary files a/.yarn/cache/ardrive-core-js-npm-1.0.4-8e5129121f-22109e31bc.zip and b/.yarn/cache/ardrive-core-js-npm-1.0.5-6e82770d54-02490de5ea.zip differ diff --git a/README.md b/README.md index 851d730b..48fc38a3 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,9 @@ ardrive upload-file --wallet-file /path/to/my/wallet.json --parent-folder-id "f0 1. [Uploading a Single File](#uploading-a-single-file) 2. [Uploading a Folder with Files](#bulk-upload) 3. [Fetching the Metadata of a File Entity](#fetching-the-metadata-of-a-file-entity) - 4. [Create New Drive and Upload Folder Pipeline Example](#create-upload-pipeline) + 4. [Uploading Manifests](#uploading-manifests) + 5. [Hosting a Webpage with Manifest](#hosting-a-webpage-with-manifest) + 6. [Create New Drive and Upload Folder Pipeline Example](#create-upload-pipeline) 7. [Other Utility Operations](#other-utility-operations) 1. [Monitoring Transactions](#monitoring-transactions) 2. [Dealing With Network Congestion](#dealing-with-network-congestion) @@ -764,6 +766,98 @@ Example output: } ``` +### Uploading Manifests + +[Arweave Path Manifests][arweave-manifests] are are special `.json` files that instruct Arweave Gateways to map file data associated with specific, unique transaction IDs to customized, hosted paths relative to that of the manifest file itself. So if, for example, your manifest file had an arweave.net URL like: + +```shell +https://arweave.net/{manifest tx id} +``` + +Then, all the mapped transactions and paths in the manifest file would be addressable at URLs like: + +```shell +https://arweave.net/{manifest tx id}/foo.txt +https://arweave.net/{manifest tx id}/bar/baz.png +``` + +ArDrive supports the creation of these Arweave manifests using any of your PUBLIC folders. The generated manifest paths will be links to each of the file entities within the specified folder. The manifest file entity will be created at the root of the folder. + +To create a manifest of an entire public drive, specify the root folder of that drive: + +```shell +ardrive create-manifest -f "bc9af866-6421-40f1-ac89-202bddb5c487" -w "/path/to/wallet" +``` + +You can also create a manifest of a folder's file entities at a custom depth by using the `--max-depth` option: + +```shell +# Create manifest of a folder's local file contents, excluding all sub-folders +ardrive create-manifest --max-depth 0 -f "867228d8-4413-4c0e-a499-e1decbf2ea38" -w "/path/to/wallet" +``` + +Creating a `.json` file of your manifest links output can be accomplished here with some `jq` parsing and piping to a file: + +```shell +ardrive create-manifest -w /path/to/wallet -f "6c312b3e-4778-4a18-8243-f2b346f5e7cb" | jq '{links}' > links.json +``` + +The manifest data transaction is tagged with a unique content-type, `application/x.arweave-manifest+json`, which tells the gateway to treat this file as a manifest. The manifest file itself is a `.json` file that holds the paths (the data transaction ids) to each file within the specified folder. + +When your folder is later changed by adding files or updating them with new revisions, the original manifest will NOT be updated on its own. A manifest is a permanent record of your files in their current state. + +However, creating a subsequent manifest with the same manifest name will create a new revision of that manifest in its new current state. Manifests follow the same name conflict resolution as outlined for files above (upsert by default). + +#### Hosting a Webpage with Manifest + +When creating a manifest, it is possible to host a webpage or web app. You can do this by creating a manifest on a folder that has an `index.html` file in its root. + +Using generated build folders from popular frameworks works as well. One requirement here to note is that the `href=` paths from your generated `index.html` file must not have leading a `/`. This means that the manifest will not resolve a path of `/dist/index.js` but it will resolve `dist/index.js` or `./dist/index.js`. + +As an example, here is a flow of creating a React app and hosting it with an ArDrive Manifest. First, generate a React app: + +```shell +yarn create react-app my-app +``` + +Next, add this field to the generated `package.json` so that the paths will resolve correctly: + +```json +"homepage": ".", +``` + +Then, create an optimized production build from within the app's directory: + +```shell +yarn build +``` + +Now, we can create and upload that produced build folder on ArDrive to any of your existing ArFS folder entities: + +```shell +ardrive upload-file -l "/build" -w "/path/to/wallet" --parent-folder-id "bc9af866-6421-40f1-ac89-202bddb5c487" +``` + +And finally, create the manifest using the generated Folder ID from the build folder creation: + +```shell +# Create manifest using the Folder ID of the `/build` folder +ardrive create-manifest -f "41759f05-614d-45ad-846b-63f3767504a4" -w "/path/to/wallet" +``` + +In the return output, the top link will be a link to the deployed web app: + +```shell + "links": [ + "https://arweave.net/0MK68J8TqGhaaOpPe713Zn0jdpczMt2NGS2CtRYiuAg", + "https://arweave.net/0MK68J8TqGhaaOpPe713Zn0jdpczMt2NGS2CtRYiuAg/asset-manifest.json", + "https://arweave.net/0MK68J8TqGhaaOpPe713Zn0jdpczMt2NGS2CtRYiuAg/favicon.ico", + "https://arweave.net/0MK68J8TqGhaaOpPe713Zn0jdpczMt2NGS2CtRYiuAg/index.html", + # ... +``` + +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]. + ### Create New Drive and Upload Folder Pipeline Example ```shell @@ -895,6 +989,7 @@ Write ArFS create-drive create-folder upload-file +create-manifest move-file move-folder @@ -959,3 +1054,5 @@ ardrive --help [ardrive-discord]: https://discord.gg/w4vvrezD [arconnect]: https://arconnect.io/ [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 diff --git a/package.json b/package.json index afe9e26d..6a090c9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ardrive-cli", - "version": "1.1.4", + "version": "1.1.5", "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.0.4", + "ardrive-core-js": "1.0.5", "arweave": "^1.10.16", "axios": "^0.21.1", "commander": "^8.2.0", diff --git a/src/commands/create_manifest.ts b/src/commands/create_manifest.ts new file mode 100644 index 00000000..70c6c3c2 --- /dev/null +++ b/src/commands/create_manifest.ts @@ -0,0 +1,57 @@ +import { EID } from 'ardrive-core-js'; +import { cliArDriveFactory, cliWalletDao } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { CLIAction } from '../CLICommand/action'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/error_codes'; +import { + BoostParameter, + FolderIdParameter, + DryRunParameter, + SeedPhraseParameter, + TreeDepthParams, + WalletFileParameter, + DestinationManifestNameParameter, + ConflictResolutionParams +} from '../parameter_declarations'; + +new CLICommand({ + name: 'create-manifest', + parameters: [ + FolderIdParameter, + DestinationManifestNameParameter, + BoostParameter, + DryRunParameter, + WalletFileParameter, + SeedPhraseParameter, + ...ConflictResolutionParams, + ...TreeDepthParams + ], + action: new CLIAction(async function action(options) { + const parameters = new ParametersHelper(options, cliWalletDao); + + const wallet = await parameters.getRequiredWallet(); + + const arDrive = cliArDriveFactory({ + wallet: wallet, + feeMultiple: parameters.getOptionalBoostSetting(), + dryRun: !!options.dryRun + }); + + const folderId = parameters.getRequiredParameterValue(FolderIdParameter, EID); + + const maxDepth = await parameters.getMaxDepth(Number.MAX_SAFE_INTEGER); + const destManifestName = parameters.getParameterValue(DestinationManifestNameParameter); + const conflictResolution = parameters.getFileNameConflictResolution(); + + const result = await arDrive.uploadPublicManifest({ + folderId: folderId, + maxDepth, + destManifestName, + conflictResolution + }); + + console.log(JSON.stringify(result, null, 4)); + + return SUCCESS_EXIT_CODE; + }) +}); diff --git a/src/commands/index.ts b/src/commands/index.ts index c7992096..653a4fb3 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -2,6 +2,7 @@ import '../parameter_declarations'; import './base_reward'; import './create_drive'; import './create_folder'; +import './create_manifest'; import './create_tx'; import './drive_info'; import './file_info'; diff --git a/src/parameter_declarations.ts b/src/parameter_declarations.ts index 9c96964c..39e3cf46 100644 --- a/src/parameter_declarations.ts +++ b/src/parameter_declarations.ts @@ -21,6 +21,7 @@ export const FileIdParameter = 'fileId'; export const ParentFolderIdParameter = 'parentFolderId'; export const LocalFilePathParameter = 'localFilePath'; export const DestinationFileNameParameter = 'destFileName'; +export const DestinationManifestNameParameter = 'destManifestName'; export const LocalFilesParameter = 'localFiles'; export const GetAllRevisionsParameter = 'getAllRevisions'; export const AllParameter = 'all'; @@ -236,6 +237,12 @@ Parameter.declare({ description: `(OPTIONAL) a destination file name to use when uploaded to ArDrive` }); +Parameter.declare({ + name: DestinationManifestNameParameter, + aliases: ['-n', '--dest-manifest-name'], + description: `(OPTIONAL) a destination file name for the manifest to use when uploaded to ArDrive` +}); + Parameter.declare({ name: LocalFilesParameter, aliases: ['--local-files'], diff --git a/yarn.lock b/yarn.lock index f985ef5a..443933b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -998,7 +998,7 @@ __metadata: "@types/source-map-support": ^0 "@typescript-eslint/eslint-plugin": ^4.18.0 "@typescript-eslint/parser": ^4.18.0 - ardrive-core-js: 1.0.4 + ardrive-core-js: 1.0.5 arweave: ^1.10.16 axios: ^0.21.1 chai: ^4.3.4 @@ -1024,9 +1024,9 @@ __metadata: languageName: unknown linkType: soft -"ardrive-core-js@npm:1.0.4": - version: 1.0.4 - resolution: "ardrive-core-js@npm:1.0.4" +"ardrive-core-js@npm:1.0.5": + version: 1.0.5 + resolution: "ardrive-core-js@npm:1.0.5" dependencies: arweave: ^1.10.16 arweave-bundles: ^1.0.3 @@ -1042,7 +1042,7 @@ __metadata: smartweave: ^0.4.45 utf8: ^3.0.0 uuid: ^8.3.2 - checksum: 22109e31bc9264580fc53788f4b5b2217a28ede4e1f0b6e2401762c25eb54c0617ea2da8c03a1b5868691aac4f9d87c9ce799908a19b83984ab300a61a3606ad + checksum: 02490de5ea3e806f1a793389b3dbf8dc89b6caa116870b0d32b8a0591eb3cd7c02dc16ff6c4c15c418b2d7734fcdd2379f20a640ac30292d4f6db629370b3941 languageName: node linkType: hard