From bcc8fa78998508ef413eeea578308be822d9662c Mon Sep 17 00:00:00 2001 From: "ice.breaker" Date: Tue, 4 Oct 2022 10:23:09 -0500 Subject: [PATCH 1/3] fix: Fix issues around profiles, more validation. closes #112 closes #110 closes #109 closes #105 --- src/client.js | 47 +++++++++++++++------------ src/commands/delegate.js | 3 +- src/commands/id.js | 4 +-- src/commands/importDelegation.js | 4 +-- src/commands/info.js | 3 -- src/commands/insights.js | 24 +++++++------- src/commands/list.js | 16 ++++----- src/commands/register.js | 4 +-- src/commands/remove.js | 12 +++---- src/commands/switchAccount.js | 3 +- src/commands/upload.js | 18 ++-------- src/commands/uploadCars.js | 27 ++++++--------- src/commands/whoami.js | 8 ++--- src/main.js | 4 +-- src/validation.js | 56 +++++++++++++++++++++++--------- 15 files changed, 117 insertions(+), 116 deletions(-) diff --git a/src/client.js b/src/client.js index df28a98..0768ab8 100644 --- a/src/client.js +++ b/src/client.js @@ -15,27 +15,28 @@ const serialize = ({ ...data }) => const deserialize = (text) => CBOR.codec.decode(Buffer.from(text, 'binary')) // @ts-ignore -export const settings = new Conf({ - projectName: cliSettings.projectName, - fileExtension: 'cbor', - serialize, - deserialize, -}) - -const client = new W3Client({ - //@ts-ignore - serviceDID: cliSettings.W3_STORE_DID, - serviceURL: cliSettings.SERVICE_URL, - //@ts-ignore - accessDID: cliSettings.ACCESS_DID, - accessURL: cliSettings.ACCESS_URL, - settings, -}) - -export default client - -export function getClient(profile = 'main') { - const settings = new Conf({ +// export const settings = new Conf({ +// projectName: cliSettings.projectName, +// fileExtension: 'cbor', +// serialize, +// deserialize, +// }) + +// const client = new W3Client({ +// //@ts-ignore +// serviceDID: cliSettings.W3_STORE_DID, +// serviceURL: cliSettings.SERVICE_URL, +// //@ts-ignore +// accessDID: cliSettings.ACCESS_DID, +// accessURL: cliSettings.ACCESS_URL, +// settings, +// }) +// +// export default client + +export function getSettings(profile = 'main') { + // @ts-ignore + return new Conf({ projectName: 'w3up', projectSuffix: '', configName: profile, @@ -43,6 +44,10 @@ export function getClient(profile = 'main') { serialize, deserialize, }) +} + +export function getClient(profile = 'main') { + const settings = getSettings(profile) const client = new W3Client({ //@ts-ignore diff --git a/src/commands/delegate.js b/src/commands/delegate.js index 3d9d166..4e4ee94 100644 --- a/src/commands/delegate.js +++ b/src/commands/delegate.js @@ -2,6 +2,7 @@ import fs from 'fs' import ora from 'ora' import { getClient } from '../client.js' +import { hasSetupAccount } from '../validation.js' /** * @typedef {import('yargs').Arguments<{did?:string, profile: string}>} DelegateArgs @@ -26,7 +27,7 @@ const exe = async ({ did, profile }) => { * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -const build = (yargs) => yargs +const build = (yargs) => yargs.check(hasSetupAccount) const id = { command: 'delegate ', diff --git a/src/commands/id.js b/src/commands/id.js index 1f091f8..2fa09e2 100644 --- a/src/commands/id.js +++ b/src/commands/id.js @@ -1,6 +1,6 @@ import ora from 'ora' -import { getClient, settings } from '../client.js' +import { getClient, getSettings } from '../client.js' /** * @typedef {{reset?:boolean, profile: string}} Id @@ -16,7 +16,7 @@ const exe = async (args) => { const view = ora({ spinner: 'line' }) if (args.reset) { - settings.clear() + getSettings(args.profile)?.clear() } const client = getClient(args.profile) diff --git a/src/commands/importDelegation.js b/src/commands/importDelegation.js index fceb34c..967c6c6 100644 --- a/src/commands/importDelegation.js +++ b/src/commands/importDelegation.js @@ -2,7 +2,7 @@ import fs from 'fs' import ora from 'ora' import { getClient } from '../client.js' -import { isPath } from '../validation.js' +import { hasID, isPath } from '../validation.js' /** * @typedef {{fileName?:string, alias?:string, profile: string}} ImportDelegation @@ -36,7 +36,7 @@ const exe = async ({ fileName, alias = '', profile }) => { * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -const builder = (yargs) => yargs.check(checkFileName) +const builder = (yargs) => yargs.check(hasID).check(checkFileName) /** * diff --git a/src/commands/info.js b/src/commands/info.js index 51282ee..8f0b1a3 100644 --- a/src/commands/info.js +++ b/src/commands/info.js @@ -1,6 +1,3 @@ -import ora, { oraPromise } from 'ora' - -import { settings } from '../client.js' import { default as info } from '../settings.js' /** diff --git a/src/commands/insights.js b/src/commands/insights.js index d066115..dd45cbb 100644 --- a/src/commands/insights.js +++ b/src/commands/insights.js @@ -3,11 +3,11 @@ import * as API from '@ucanto/interface' import { parseLink } from '@ucanto/server' import ora from 'ora' -import client from '../client.js' -import { hasID, isCID } from '../validation.js' +import { getClient } from '../client.js' +import { hasSetupAccount, isCID } from '../validation.js' /** - * @typedef {{cid?:API.Link, ws?:boolean, subscribe?:boolean, insight_data?: any}} Insights + * @typedef {{cid?:API.Link, ws?:boolean, subscribe?:boolean, insight_data?: any, profile?:string}} Insights * @typedef {import('yargs').Arguments} InsightsArgs */ @@ -16,13 +16,14 @@ import { hasID, isCID } from '../validation.js' * @param {InsightsArgs} argv * @returns {Promise} */ -const exe = async ({ cid, ws, subscribe }) => { +const exe = async ({ cid, ws, subscribe, profile }) => { const spinner = ora({ text: `Getting insights for ${cid}`, spinner: 'line' }) const shouldWS = ws || subscribe if (shouldWS) { spinner.fail(`⚠️Subscriptions not yet supported ⚠️`) } else { + const client = getClient(profile) /*** * @type Insights */ @@ -34,15 +35,12 @@ const exe = async ({ cid, ws, subscribe }) => { * @type {import('yargs').CommandBuilder} yargs */ const builder = (yargs) => - yargs - .check(() => hasID()) - .check(checkCID) - .option('subscribe', { - type: 'boolean', - alias: 'ws', - showInHelp: true, - describe: 'Get a Subscription to incoming insights', - }) + yargs.check(hasSetupAccount).check(checkCID).option('subscribe', { + type: 'boolean', + alias: 'ws', + showInHelp: true, + describe: 'Get a Subscription to incoming insights', + }) /** * @param {InsightsArgs} argv diff --git a/src/commands/list.js b/src/commands/list.js index 17f0059..313916c 100644 --- a/src/commands/list.js +++ b/src/commands/list.js @@ -2,7 +2,7 @@ import ora, { oraPromise } from 'ora' import { getClient } from '../client.js' import { buildSimpleConsoleTable } from '../utils.js' -import { hasID } from '../validation.js' +import { hasID, hasSetupAccount } from '../validation.js' /** * @typedef {{verbose?:boolean, profile: string}} List @@ -90,14 +90,12 @@ const exe = async (argv) => { /** @type {import('yargs').CommandBuilder} yargs */ const builder = (yargs) => - yargs - // .check(() => hasID()) - .option('verbose', { - type: 'boolean', - alias: 'verbose', - showInHelp: true, - describe: 'Show more columns in the list, such as the Uploaded CAR CID', - }) + yargs.check(hasSetupAccount).option('verbose', { + type: 'boolean', + alias: 'verbose', + showInHelp: true, + describe: 'Show more columns in the list, such as the Uploaded CAR CID', + }) export default { command: 'list', diff --git a/src/commands/register.js b/src/commands/register.js index b82e608..b8038cc 100644 --- a/src/commands/register.js +++ b/src/commands/register.js @@ -42,7 +42,7 @@ const exe = async (argv) => { * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -// const builder = (yargs) => yargs.check(() => hasID()).check(checkEmail) +const builder = (yargs) => yargs.check(hasID).check(checkEmail) /** * @param {RegisterArgs} argv @@ -58,6 +58,6 @@ const checkEmail = (argv) => { export default { command: 'register ', describe: 'Register your UCAN Identity with w3up', - // builder, + builder, handler: exe, } diff --git a/src/commands/remove.js b/src/commands/remove.js index 7f5f58b..603908c 100644 --- a/src/commands/remove.js +++ b/src/commands/remove.js @@ -3,11 +3,11 @@ import * as API from '@ucanto/interface' import { parseLink } from '@ucanto/server' import ora from 'ora' -import client from '../client.js' -import { hasID, isCID } from '../validation.js' +import { getClient } from '../client.js' +import { hasID, hasSetupAccount, isCID } from '../validation.js' /** - * @typedef {{cid?:API.Link}} Remove + * @typedef {{cid?:API.Link, profile?:string}} Remove * @typedef {import('yargs').Arguments} RemoveArgs */ @@ -16,9 +16,9 @@ import { hasID, isCID } from '../validation.js' * @param {RemoveArgs} argv * @returns {Promise} */ -const exe = async ({ cid }) => { +const exe = async ({ cid, profile }) => { const view = ora(`Unlinking ${cid}...`).start() - const res = await client.remove(parseLink(cid)) + const res = await getClient(profile).remove(parseLink(cid)) view.succeed(`${res.toString()}`) } @@ -26,7 +26,7 @@ const exe = async ({ cid }) => { * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -const builder = (yargs) => yargs.check(() => hasID()).check(checkCID) +const builder = (yargs) => yargs.check(hasSetupAccount).check(checkCID) /** * @param {RemoveArgs} argv diff --git a/src/commands/switchAccount.js b/src/commands/switchAccount.js index cbd959c..101df8d 100644 --- a/src/commands/switchAccount.js +++ b/src/commands/switchAccount.js @@ -3,6 +3,7 @@ import { Delegation } from '@ucanto/server' import inquirer from 'inquirer' import { getClient } from '../client.js' +import { hasID } from '../validation.js' import listAccounts from './listAccounts.js' /** @@ -77,7 +78,7 @@ async function inquirerPick(choices, client) { * @returns {import('yargs').Argv<{}>} */ const builder = (yargs) => - yargs.option('did', { + yargs.check(hasID).option('did', { type: 'string', showInHelp: true, describe: 'select account by did', diff --git a/src/commands/upload.js b/src/commands/upload.js index 328e8fe..4074b71 100644 --- a/src/commands/upload.js +++ b/src/commands/upload.js @@ -9,9 +9,8 @@ import toIterator from 'stream-to-it' import { getClient } from '../client.js' import { buildCar } from '../lib/car/buildCar.js' import { logToFile } from '../lib/logging.js' -import { MAX_CAR_SIZE } from '../settings.js' import { bytesToCarCID } from '../utils.js' -import { hasID, isPath, resolvePath } from '../validation.js' +import { checkPath, hasID, hasSetupAccount } from '../validation.js' /** * @typedef {{path?: string;split?: boolean; profile: string}} Upload @@ -109,7 +108,7 @@ const exe = async (argv) => { */ const builder = (yargs) => yargs - // .check(() => hasID()) + .check(hasSetupAccount) .check(checkPath) .option('chunk-size', { type: 'number', @@ -122,19 +121,6 @@ const builder = (yargs) => 'Split the data into multiple when cars when size limit is hit.', }) -/** - * @param {UploadArgs} argv - */ -const checkPath = ({ path }) => { - try { - return isPath(path) - } catch (err) { - throw new Error( - `${path} is probably not a valid path to a file or directory: \n${err}` - ) - } -} - export default { command: ['upload ', 'import '], describe: 'Upload any file or directory to your account', diff --git a/src/commands/uploadCars.js b/src/commands/uploadCars.js index b2e84e7..7646da3 100644 --- a/src/commands/uploadCars.js +++ b/src/commands/uploadCars.js @@ -4,11 +4,17 @@ import path from 'path' // @ts-ignore import toIterator from 'stream-to-it' -import client from '../client.js' +import { getClient } from '../client.js' import { getAllFiles, isDirectory } from '../lib/car/file.js' import { logToFile } from '../lib/logging.js' import { MAX_CAR_SIZE } from '../settings.js' -import { hasID, isCarFile, isPath, resolvePath } from '../validation.js' +import { + checkPath, + hasID, + hasSetupAccount, + isCarFile, + resolvePath, +} from '../validation.js' //gotta start somewhere. 3 is fine. const MAX_CONNECTION_POOL_SIZE = 3 @@ -43,7 +49,7 @@ export async function uploadExistingCar(filePath, view) { } /** - * @typedef {{path?:string}} Upload + * @typedef {{path?:string, profile?:string}} Upload * @typedef {import('yargs').Arguments} UploadArgs */ @@ -99,20 +105,7 @@ const exe = async (argv) => { * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -const builder = (yargs) => yargs.check(() => hasID()).check(checkPath) - -/** - * @param {UploadArgs} argv - */ -const checkPath = ({ path }) => { - try { - return isPath(path) - } catch (err) { - throw new Error( - `${path} is probably not a valid path to a file or directory: \n${err}` - ) - } -} +const builder = (yargs) => yargs.check(hasSetupAccount).check(checkPath) export default { command: ['upload-cars '], diff --git a/src/commands/whoami.js b/src/commands/whoami.js index 264f9e6..db61d03 100644 --- a/src/commands/whoami.js +++ b/src/commands/whoami.js @@ -1,14 +1,12 @@ import ora from 'ora' import { getClient } from '../client.js' -import { logToFile } from '../lib/logging.js' -import { hasID } from '../validation.js' +import { hasSetupAccount } from '../validation.js' const exe = async (/** @type {{ profile: string | undefined; }} */ args) => { const view = ora({ text: 'Checking identity', spinner: 'line' }) try { const client = getClient(args.profile) - hasID(client) const { agent, account } = await client.identity() const response = await client.whoami() @@ -34,12 +32,12 @@ Access Account: ${response}`) * @type {import('yargs').CommandBuilder} yargs * @returns {import('yargs').Argv<{}>} */ -const builder = (yargs) => yargs.check(() => hasID()) +const builder = (yargs) => yargs.check(hasSetupAccount) export default { command: 'whoami', describe: 'Show your current UCAN Identity', - // builder, + builder, handler: exe, exampleOut: `DID:12345...`, exampleIn: '$0 whoami', diff --git a/src/main.js b/src/main.js index 05c5168..1f2b523 100644 --- a/src/main.js +++ b/src/main.js @@ -54,7 +54,6 @@ export const main = async () => { //registration .command(id) - // .command(setup) // .example(id.exampleIn, id.exampleOut) .command(register) @@ -83,7 +82,8 @@ export const main = async () => { .command(switchAccount) //insights - .command(insights) + // false description hides command. + .command(insights.command, false, insights.handler) //utilities .command(inspectCar) diff --git a/src/validation.js b/src/validation.js index 0281572..cc71dbf 100644 --- a/src/validation.js +++ b/src/validation.js @@ -5,7 +5,7 @@ import fs from 'fs' import path from 'path' import { pathToFileURL } from 'url' -import { settings } from './client.js' +import { getSettings } from './client.js' /** * @@ -23,15 +23,11 @@ export const isEmail = (email) => { * @returns {boolean} */ export const isCID = (cid) => { - try { - if (!cid) { - throw 'A CID was not provided' - } - parseLink(cid) - return true - } catch (err) { - throw err + if (!cid) { + throw 'A CID was not provided' } + parseLink(cid) + return true } /** @@ -59,17 +55,38 @@ export const isPath = (targetPath) => { return false } -export const hasID = (client) => { - if ( - !client.settings.has('secret') && - !client.settings.has('account_secret') - ) { +/** + * @param {import('yargs').Arguments<{path?:string}>} argv + */ +export const checkPath = ({ path }) => { + try { + return isPath(path) + } catch (err) { + throw new Error( + `${path} is probably not a valid path to a file or directory: \n${err}` + ) + } +} + +/** + * @param {import('yargs').Arguments<{profile?:string}>} argv + */ +export const hasID = ({ profile }) => { + const settings = getSettings(profile) + + if (!settings.has('secret') && !settings.has('account_secret')) { throw new Error(`You have not setup an id, please run w3up id first.`) } return true } -export const hasEmail = (client) => { - if (!client.settings.has('email')) { + +/** + * @param {import('yargs').Arguments<{profile?:string}>} argv + */ +export const hasEmail = ({ profile }) => { + const settings = getSettings(profile) + + if (!settings.has('email')) { throw new Error( `You have not setup an email, please run w3up register first.` ) @@ -77,6 +94,13 @@ export const hasEmail = (client) => { return true } +/** + * @param {import('yargs').Arguments<{profile?:string}>} argv + */ +export function hasSetupAccount(argv) { + return hasID(argv) && hasEmail(argv) +} + /** * * @param {string | undefined} targetPath From e7aab80af783b702647add087a24e3b7a153812f Mon Sep 17 00:00:00 2001 From: "ice.breaker" Date: Tue, 4 Oct 2022 11:22:04 -0500 Subject: [PATCH 2/3] fix: Allow merging of old settings when moving to profile based system. closes #114 --- src/client.js | 49 ++++++++++++++++++++++++---------------------- src/commands/id.js | 4 ++-- src/validation.js | 6 +++--- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/client.js b/src/client.js index 0768ab8..1a9cd57 100644 --- a/src/client.js +++ b/src/client.js @@ -14,29 +14,27 @@ const serialize = ({ ...data }) => */ const deserialize = (text) => CBOR.codec.decode(Buffer.from(text, 'binary')) -// @ts-ignore -// export const settings = new Conf({ -// projectName: cliSettings.projectName, -// fileExtension: 'cbor', -// serialize, -// deserialize, -// }) - -// const client = new W3Client({ -// //@ts-ignore -// serviceDID: cliSettings.W3_STORE_DID, -// serviceURL: cliSettings.SERVICE_URL, -// //@ts-ignore -// accessDID: cliSettings.ACCESS_DID, -// accessURL: cliSettings.ACCESS_URL, -// settings, -// }) -// -// export default client - -export function getSettings(profile = 'main') { +// TODO this will go away later? +function mergeSettings(profileSettings) { + if (profileSettings.size) { + return + } + + const oldSettings = new Conf({ + projectName: cliSettings.projectName, + fileExtension: 'cbor', + serialize, + deserialize, + }) + + if (oldSettings.size) { + profileSettings.store = { ...oldSettings.store } + } +} + +export function getProfileSettings(profile = 'main') { // @ts-ignore - return new Conf({ + const profileSettings = new Conf({ projectName: 'w3up', projectSuffix: '', configName: profile, @@ -44,10 +42,15 @@ export function getSettings(profile = 'main') { serialize, deserialize, }) + + // TODO: remove this when no longer needed. + mergeSettings(profileSettings) + + return profileSettings } export function getClient(profile = 'main') { - const settings = getSettings(profile) + const settings = getProfileSettings(profile) const client = new W3Client({ //@ts-ignore diff --git a/src/commands/id.js b/src/commands/id.js index 2fa09e2..3396e0f 100644 --- a/src/commands/id.js +++ b/src/commands/id.js @@ -1,6 +1,6 @@ import ora from 'ora' -import { getClient, getSettings } from '../client.js' +import { getClient, getProfileSettings } from '../client.js' /** * @typedef {{reset?:boolean, profile: string}} Id @@ -16,7 +16,7 @@ const exe = async (args) => { const view = ora({ spinner: 'line' }) if (args.reset) { - getSettings(args.profile)?.clear() + getProfileSettings(args.profile)?.clear() } const client = getClient(args.profile) diff --git a/src/validation.js b/src/validation.js index cc71dbf..e25b074 100644 --- a/src/validation.js +++ b/src/validation.js @@ -5,7 +5,7 @@ import fs from 'fs' import path from 'path' import { pathToFileURL } from 'url' -import { getSettings } from './client.js' +import { getProfileSettings } from './client.js' /** * @@ -72,7 +72,7 @@ export const checkPath = ({ path }) => { * @param {import('yargs').Arguments<{profile?:string}>} argv */ export const hasID = ({ profile }) => { - const settings = getSettings(profile) + const settings = getProfileSettings(profile) if (!settings.has('secret') && !settings.has('account_secret')) { throw new Error(`You have not setup an id, please run w3up id first.`) @@ -84,7 +84,7 @@ export const hasID = ({ profile }) => { * @param {import('yargs').Arguments<{profile?:string}>} argv */ export const hasEmail = ({ profile }) => { - const settings = getSettings(profile) + const settings = getProfileSettings(profile) if (!settings.has('email')) { throw new Error( From 805ac30dc8503d372393e13f0bc6bb0c971e63bd Mon Sep 17 00:00:00 2001 From: "ice.breaker" Date: Tue, 4 Oct 2022 12:44:32 -0500 Subject: [PATCH 3/3] feat: Get started on making chunking work the same as web3. --- src/lib/car/buildCar.js | 22 +++++++++++++++++++--- src/lib/info/carToDot.js | 20 ++++++++++++++++---- src/lib/info/carToList.js | 9 +++++++-- src/lib/info/carToTree.js | 15 +++++++++------ src/settings.js | 5 +++++ 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/lib/car/buildCar.js b/src/lib/car/buildCar.js index 622fa8c..eafe516 100644 --- a/src/lib/car/buildCar.js +++ b/src/lib/car/buildCar.js @@ -1,16 +1,18 @@ // @ts-ignore import * as CAR from '@ipld/car' import * as UnixFS from '@ipld/unixfs' +import * as FixedChunker from '@ipld/unixfs/file/chunker/fixed' import fs from 'fs' import path from 'path' import 'web-streams-polyfill' +import { ALLOW_RAW_LEAVES, UNIXFS_BLOCK_SIZE } from '../../settings.js' import { isDirectory } from '../../utils.js' import { walkDir, wrapFilesWithDir } from './dir.js' import { streamFileToBlock } from './file.js' // Internal unixfs read stream capacity that can hold around 32 blocks -const CAPACITY = UnixFS.BLOCK_SIZE_LIMIT * 32 +const CAPACITY = UNIXFS_BLOCK_SIZE * 32 const MAX_CARS_AT_ONCE = 8 /** @@ -28,8 +30,22 @@ const MAX_CARS_AT_ONCE = 8 async function createReadableBlockStreamWithWrappingDir(pathName, writable) { // Next we create a writer with filesystem like API for encoding files and // directories into IPLD blocks that will come out on `readable` end. - const writer = UnixFS.createWriter({ writable }) - // writer.settings.chunker.context.maxChunkSize = 1024 * 5 + const writer = UnixFS.createWriter({ + writable, + settings: { + // Set blocksize to match current w3 settings, 1MB + chunker: FixedChunker.withMaxChunkSize(UNIXFS_BLOCK_SIZE), + // Allow raw leaves + fileChunkEncoder: ALLOW_RAW_LEAVES + ? UnixFS.UnixFSRawLeaf + : UnixFS.UnixFSLeaf, + smallFileEncoder: ALLOW_RAW_LEAVES + ? UnixFS.UnixFSRawLeaf + : UnixFS.UnixFSLeaf, + }, + }) + + console.log('hi', writer.settings) // hold files to wrap with dir. let files = [] diff --git a/src/lib/info/carToDot.js b/src/lib/info/carToDot.js index 6ab0677..d3509b2 100644 --- a/src/lib/info/carToDot.js +++ b/src/lib/info/carToDot.js @@ -25,7 +25,7 @@ function buildLabel(obj) { } if (typeof val == 'object') { if (key == 'content') { - if (val?.type) { + if (val?.type != undefined) { const bytes = val['content'] ? `|{size|${humanizeBytes(val['content']?.byteLength)}}` : '' @@ -87,7 +87,10 @@ export async function run(bytes, vertical) { cur.content = decode(blockIndex.cid, cur.bytes) /** @type Array */ - const links = cur.content.Links || cur.content?.entries || [] + let links = cur.content.Links || cur.content?.entries || [] + if (links instanceof Function) { + links = cur?.content?.entries() + } const scid = toShortCID(cur.cid) const label = buildLabel(cur) @@ -101,7 +104,16 @@ export async function run(bytes, vertical) { dot += `\n\t"${scid}" [label="{${label}}" labeljust=l]` } - links.forEach((link) => { + for (let link of links) { + if (Array.isArray(link)) { + const ports = vertical + ? 'tailport="e" headport="w"' + : 'tailport="s" headport="n"' + + linkDot += `[${ports}]` + continue + } + const cid = link?.cid || link?.Hash const name = link?.name || link?.Name linkDot += `\n\t"${scid}" -> "${toShortCID(cid)}" ` @@ -110,7 +122,7 @@ export async function run(bytes, vertical) { : 'tailport="s" headport="n"' linkDot += `[headlabel="${name}" ${ports}]` - }) + } i++ } diff --git a/src/lib/info/carToList.js b/src/lib/info/carToList.js index 9d81d04..f320df5 100644 --- a/src/lib/info/carToList.js +++ b/src/lib/info/carToList.js @@ -28,8 +28,13 @@ export async function run(bytes) { throw 'no blocks' } - output += - blockIndex.cid.toString() + '\tz' + blockIndex.cid.toV0().toString() + const cidv1 = blockIndex.cid.toString() + let cidv0 = 'N/A' + try { + cidv0 = 'z' + blockIndex.cid.toV0() + } catch (err) {} + + output += `${cidv1}\t${cidv0}` output += '\n' } return output diff --git a/src/lib/info/carToTree.js b/src/lib/info/carToTree.js index 1e951d0..0b68a82 100644 --- a/src/lib/info/carToTree.js +++ b/src/lib/info/carToTree.js @@ -41,17 +41,19 @@ export async function run(bytes) { /** @type {{Links?:Array, entries?:Array}} */ const content = decode(blockIndex.cid, block.bytes) - const isRoot = roots.some((x) => x.toString() == blockIndex.cid.toString()) + const isRoot = roots.some( + (x) => x?.toString() == blockIndex.cid?.toString() + ) const links = content.Links || content?.entries || [] - blockMap.set(blockIndex.cid.toString(), { - cid: blockIndex.cid.toString(), + blockMap.set(blockIndex.cid?.toString(), { + cid: blockIndex.cid?.toString(), links: links, }) if (isRoot) { contentRoots.push({ - cid: blockIndex.cid.toString(), + cid: blockIndex.cid?.toString(), links: links, }) } @@ -76,14 +78,15 @@ function walkTree(cid, name, blockMap) { const label = name.length > 0 ? name : cid if (!block) { - return { label: label + 'not found', nodes: [] } + return null + // return { label: label + 'not found', nodes: [] } } return { label, nodes: block.links .map((x) => - walkTree(x.cid.toString(), x?.name || x?.Name || '', blockMap) + walkTree(x.cid?.toString(), x?.name || x?.Name || '', blockMap) ) .filter((x) => x), } diff --git a/src/settings.js b/src/settings.js index fb58322..8ca40db 100644 --- a/src/settings.js +++ b/src/settings.js @@ -10,6 +10,9 @@ export const MAX_CAR_SIZE = Math.pow(1024, 2) * 512 //512MB // export const MAX_CAR_SIZE = Math.pow(1024, 3) * 2 //2GB //1800000000 //1.8GB +export const UNIXFS_BLOCK_SIZE = Math.pow(1024, 2) // 1MB +export const ALLOW_RAW_LEAVES = true // 1MB + const projectName = 'w3-cli' const W3_STORE_DID = @@ -39,4 +42,6 @@ export default { ACCESS_URL, MAX_CAR_SIZE, MAX_CAR_SIZE_HUMANIZED: humanizeBytes(MAX_CAR_SIZE), + UNIXFS_BLOCK_SIZE, + ALLOW_RAW_LEAVES, }