filecoin-pin
Version:
Bridge IPFS content to Filecoin Onchain Cloud using familiar tools
151 lines (125 loc) • 5.01 kB
text/typescript
/**
* CLI entrypoint for removing a piece from a Data Set.
*
* Responsibilities:
* - Validate required CLI arguments (piece CID, dataSet)
* - Initialize Synapse with CLI auth/env configuration
* - Wire up progress events to spinner output
* - Return transaction hash and confirmation status (or throw on failure)
*/
import pc from 'picocolors'
import pino from 'pino'
import { TELEMETRY_CLI_APP_NAME } from '../common/constants.js'
import { type RemovePieceProgressEvents, removePiece } from '../core/piece/index.js'
import { cleanupSynapseService, createStorageContext, initializeSynapse } from '../core/synapse/index.js'
import { parseCLIAuth } from '../utils/cli-auth.js'
import { cancel, createSpinner, intro, outro } from '../utils/cli-helpers.js'
import { log } from '../utils/cli-logger.js'
import type { RmPieceOptions, RmPieceResult } from './types.js'
/**
* Run the remove piece process.
*
* @param options - CLI options including piece CID and dataSet id
* @returns Transaction hash, confirmation status, and identifiers used
*
* Behavior:
* - Requires both `piece` and `dataSet`; throws if missing/invalid
* - Uses CLI auth env/flags via parseCLIAuth
* - Streams progress to spinner and exits with cancel on failure
* - Always calls cleanupSynapseService to close providers
*/
export async function runRmPiece(options: RmPieceOptions): Promise<RmPieceResult> {
intro(pc.bold('Filecoin Pin Remove'))
const spinner = createSpinner()
// Initialize logger (silent for CLI output)
const logger = pino({
level: process.env.LOG_LEVEL || 'silent',
})
const { piece: pieceCid, dataSet } = options
// Validate inputs
if (!pieceCid || !dataSet) {
spinner.stop(`${pc.red('✗')} Piece CID and DataSet ID are required`)
cancel('Remove cancelled')
throw new Error('Piece CID and DataSet ID are required')
}
const dataSetId = Number(dataSet)
if (!Number.isInteger(dataSetId) || dataSetId <= 0) {
spinner.stop(`${pc.red('✗')} DataSet ID must be a positive integer`)
cancel('Remove cancelled')
throw new Error('DataSet ID must be a positive integer')
}
try {
spinner.start('Initializing Synapse SDK...')
const authConfig = parseCLIAuth(options)
const synapse = await initializeSynapse(
{ ...authConfig, telemetry: { sentrySetTags: { appName: TELEMETRY_CLI_APP_NAME } } },
logger
)
const network = synapse.getNetwork()
spinner.stop(`${pc.green('✓')} Connected to ${pc.bold(network)}`)
log.spinnerSection('Remove Configuration', [
pc.gray(`Piece CID: ${pieceCid}`),
pc.gray(`Data Set ID: ${dataSetId}`),
])
// Track transaction details
let txHash = ''
let isConfirmed = false
// Remove piece with progress tracking
const onProgress = (event: RemovePieceProgressEvents): void => {
switch (event.type) {
case 'remove-piece:submitting':
spinner.start('Submitting remove transaction...')
break
case 'remove-piece:submitted':
spinner.message(`Transaction submitted: ${event.data.txHash}`)
txHash = event.data.txHash
break
case 'remove-piece:confirming':
spinner.message('Waiting for transaction confirmation...')
break
case 'remove-piece:confirmation-failed':
spinner.message(`${pc.yellow('⚠')} Confirmation wait timed out: ${event.data.message}`)
break
case 'remove-piece:complete':
isConfirmed = event.data.confirmed
txHash = event.data.txHash
spinner.stop(`${pc.green('✓')} Piece removed${isConfirmed ? ' and confirmed' : ' (confirmation pending)'}`)
break
}
}
spinner.start('Creating storage context...')
const { storage } = await createStorageContext(synapse, { logger, dataset: { useExisting: dataSetId } })
spinner.stop(`${pc.green('✓')} Storage context created`)
spinner.start('Removing piece...')
txHash = await removePiece(pieceCid, storage, {
synapse,
logger,
onProgress,
waitForConfirmation: options.waitForConfirmation ?? false,
})
// Display results
log.spinnerSection('Results', [
pc.gray(`Transaction Hash: ${txHash}`),
pc.gray(`Status: ${isConfirmed ? 'Confirmed' : 'Pending confirmation'}`),
pc.gray(`Network: ${network}`),
])
const result: RmPieceResult = {
pieceCid,
dataSetId,
transactionHash: txHash,
confirmed: isConfirmed,
}
// Clean up WebSocket providers to allow process termination
await cleanupSynapseService()
outro('Remove completed successfully')
return result
} catch (error) {
spinner.stop(`${pc.red('✗')} Remove failed: ${error instanceof Error ? error.message : 'Unknown error'}`)
logger.error({ event: 'rm.failed', error }, 'Remove failed')
cancel('Remove failed')
throw error
} finally {
// Always cleanup WebSocket providers
await cleanupSynapseService()
}
}