text: /*
   * Copyright IBM Corp. All Rights Reserved.
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  'use strict';
  
  /**
   * Application that shows events when creating and updating an asset
   *   -- How to register a contract listener for chaincode events
   *   -- How to get the chaincode event name and value from the chaincode event
   *   -- How to retrieve the transaction and block information from the chaincode event
   *   -- How to register a block listener for full block events
   *   -- How to retrieve the transaction and block information from the full block event
   *   -- How to register to recieve private data associated with transactions when
   *      registering a block listener
   *   -- How to retreive the private data from the full block event
   *   -- The listener will be notified of an event at anytime. Notice that events will
   *      be posted by the listener after the application activity causing the ledger change
   *      and during other application activity unrelated to the event
   *   -- How to connect to a Gateway that will not use events when submitting transactions.
   *      This may be useful when the application does not want to wait for the peer to commit
   *      blocks and notify the application.
   *
   * To see the SDK workings, try setting the logging to be displayed on the console
   * before executing this application.
   *        export HFC_LOGGING='{"debug":"console"}'
   * See the following on how the SDK is working with the Peer's Event Services
   * https://hyperledger-fabric.readthedocs.io/en/latest/peer_event_services.html
   *
   * See the following for more details on using the Node SDK
   * https://hyperledger.github.io/fabric-sdk-node/release-2.2/module-fabric-network.html
   */
  
  // pre-requisites:
  // - fabric-sample two organization test-network setup with two peers, ordering service,
  //   and 2 certificate authorities
  //         ===> from directory test-network
  //         ./network.sh up createChannel -ca
  //
  // - Use the asset-transfer-events/chaincode-javascript chaincode deployed on
  //   the channel "mychannel". The following deploy command will package, install,
  //   approve, and commit the javascript chaincode, all the actions it takes
  //   to deploy a chaincode to a channel.
  //         ===> from directory test-network
  //         ./network.sh deployCC -ccn events -ccp ../asset-transfer-events/chaincode-javacript/ -ccl javascript -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
  //
  // - Be sure that node.js is installed
  //         ===> from directory asset-transfer-sbe/application-javascript
  //         node -v
  // - npm installed code dependencies
  //         ===> from directory asset-transfer-sbe/application-javascript
  //         npm install
  // - to run this test application
  //         ===> from directory asset-transfer-sbe/application-javascript
  //         node app.js
  
  // NOTE: If you see an error like these:
  /*
  
     Error in setup: Error: DiscoveryService: mychannel error: access denied
  
     OR
  
     Failed to register user : Error: fabric-ca request register failed with errors [[ { code: 20, message: 'Authentication failure' } ]]
  
  	*/
  // Delete the /fabric-samples/asset-transfer-sbe/application-javascript/wallet directory
  // and retry this application.
  //
  // The certificate authority must have been restarted and the saved certificates for the
  // admin and application user are not valid. Deleting the wallet store will force these to be reset
  // with the new certificate authority.
  //
  
  // use this to set logging, must be set before the require('fabric-network');
  process.env.HFC_LOGGING = '{"debug": "./debug.log"}';
  
  const { Gateway, Wallets } = require('fabric-network');
  const EventStrategies = require('fabric-network/lib/impl/event/defaulteventhandlerstrategies');
  const FabricCAServices = require('fabric-ca-client');
  const path = require('path');
  const { buildCAClient, registerAndEnrollUser, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
  const { buildCCPOrg1, buildWallet } = require('../../test-application/javascript/AppUtil.js');
  
  const channelName = 'mychannel';
  const chaincodeName = 'events';
  
  const org1 = 'Org1MSP';
  const Org1UserId = 'appUser1';
  
  const RED = '\x1b[31m\n';
  const GREEN = '\x1b[32m\n';
  const BLUE = '\x1b[34m';
  const RESET = '\x1b[0m';
  
  /**
   * Perform a sleep -- asynchronous wait
   * @param ms the time in milliseconds to sleep for
   */
  function sleep(ms) {
  	return new Promise((resolve) => setTimeout(resolve, ms));
  }
  
  async function initGatewayForOrg1(useCommitEvents) {
  	console.log(`${GREEN}--> Fabric client user & Gateway init: Using Org1 identity to Org1 Peer${RESET}`);
  	// build an in memory object with the network configuration (also known as a connection profile)
  	const ccpOrg1 = buildCCPOrg1();
  
  	// build an instance of the fabric ca services client based on
  	// the information in the network configuration
  	const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
  
  	// setup the wallet to cache the credentials of the application user, on the app server locally
  	const walletPathOrg1 = path.join(__dirname, 'wallet', 'org1');
  	const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
  
  	// in a real application this would be done on an administrative flow, and only once
  	// stores admin identity in local wallet, if needed
  	await enrollAdmin(caOrg1Client, walletOrg1, org1);
  	// register & enroll application user with CA, which is used as client identify to make chaincode calls
  	// and stores app user identity in local wallet
  	// In a real application this would be done only when a new user was required to be added
  	// and would be part of an administrative flow
  	await registerAndEnrollUser(caOrg1Client, walletOrg1, org1, Org1UserId, 'org1.department1');
  
  	try {
  		// Create a new gateway for connecting to Org's peer node.
  		const gatewayOrg1 = new Gateway();
  
  		if (useCommitEvents) {
  			await gatewayOrg1.connect(ccpOrg1, {
  				wallet: walletOrg1,
  				identity: Org1UserId,
  				discovery: { enabled: true, asLocalhost: true }
  			});
  		} else {
  			await gatewayOrg1.connect(ccpOrg1, {
  				wallet: walletOrg1,
  				identity: Org1UserId,
  				discovery: { enabled: true, asLocalhost: true },
  				eventHandlerOptions: EventStrategies.NONE
  			});
  		}
  
  
  		return gatewayOrg1;
  	} catch (error) {
  		console.error(`Error in connecting to gateway for Org1: ${error}`);
  		process.exit(1);
  	}
  }
  
  function checkAsset(org, resultBuffer, color, size, owner, appraisedValue, price) {
  	console.log(`${GREEN}<-- Query results from ${org}${RESET}`);
  
  	let asset;
  	if (resultBuffer) {
  		asset = JSON.parse(resultBuffer.toString('utf8'));
  	} else {
  		console.log(`${RED}*** Failed to read asset${RESET}`);
  	}
  	console.log(`*** verify asset ${asset.ID}`);
  
  	if (asset) {
  		if (asset.Color === color) {
  			console.log(`*** asset ${asset.ID} has color ${asset.Color}`);
  		} else {
  			console.log(`${RED}*** asset ${asset.ID} has color of ${asset.Color}${RESET}`);
  		}
  		if (asset.Size === size) {
  			console.log(`*** asset ${asset.ID} has size ${asset.Size}`);
  		} else {
  			console.log(`${RED}*** Failed size check from ${org} - asset ${asset.ID} has size of ${asset.Size}${RESET}`);
  		}
  		if (asset.Owner === owner) {
  			console.log(`*** asset ${asset.ID} owned by ${asset.Owner}`);
  		} else {
  			console.log(`${RED}*** Failed owner check from ${org} - asset ${asset.ID} owned by ${asset.Owner}${RESET}`);
  		}
  		if (asset.AppraisedValue === appraisedValue) {
  			console.log(`*** asset ${asset.ID} has appraised value ${asset.AppraisedValue}`);
  		} else {
  			console.log(`${RED}*** Failed appraised value check from ${org} - asset ${asset.ID} has appraised value of ${asset.AppraisedValue}${RESET}`);
  		}
  		if (price) {
  			if (asset.asset_properties && asset.asset_properties.Price === price) {
  				console.log(`*** asset ${asset.ID} has price ${asset.asset_properties.Price}`);
  			} else {
  				console.log(`${RED}*** Failed price check from ${org} - asset ${asset.ID} has price of ${asset.asset_properties.Price}${RESET}`);
  			}
  		}
  	}
  }
  
  function showTransactionData(transactionData) {
  	const creator = transactionData.actions[0].header.creator;
  	console.log(`    - submitted by: ${creator.mspid}-${creator.id_bytes.toString('hex')}`);
  	for (const endorsement of transactionData.actions[0].payload.action.endorsements) {
  		console.log(`    - endorsed by: ${endorsement.endorser.mspid}-${endorsement.endorser.id_bytes.toString('hex')}`);
  	}
  	const chaincode = transactionData.actions[0].payload.chaincode_proposal_payload.input.chaincode_spec;
  	console.log(`    - chaincode:${chaincode.chaincode_id.name}`);
  	console.log(`    - function:${chaincode.input.args[0].toString()}`);
  	for (let x = 1; x < chaincode.input.args.length; x++) {
  		console.log(`    - arg:${chaincode.input.args[x].toString()}`);
  	}
  }
  
  async function main() {
  	console.log(`${BLUE} **** START ****${RESET}`);
  	try {
  		let randomNumber = Math.floor(Math.random() * 1000) + 1;
  		// use a random key so that we can run multiple times
  		let assetKey = `item-${randomNumber}`;
  
  		/** ******* Fabric client init: Using Org1 identity to Org1 Peer ******* */
  		const gateway1Org1 = await initGatewayForOrg1(true); // transaction handling uses commit events
  		const gateway2Org1 = await initGatewayForOrg1();
  
  		try {
  			//
  			//  - - - - - -  C H A I N C O D E  E V E N T S
  			//
  			console.log(`${BLUE} **** CHAINCODE EVENTS ****${RESET}`);
  			let transaction;
  			let listener;
  			const network1Org1 = await gateway1Org1.getNetwork(channelName);
  			const contract1Org1 = network1Org1.getContract(chaincodeName);
  
  			try {
  				// first create a listener to be notified of chaincode code events
  				// coming from the chaincode ID "events"
  				listener = async (event) => {
  					// The payload of the chaincode event is the value place there by the
  					// chaincode. Notice it is a byte data and the application will have
  					// to know how to deserialize.
  					// In this case we know that the chaincode will always place the asset
  					// being worked with as the payload for all events produced.
  					const asset = JSON.parse(event.payload.toString());
  					console.log(`${GREEN}<-- Contract Event Received: ${event.eventName} - ${JSON.stringify(asset)}${RESET}`);
  					// show the information available with the event
  					console.log(`*** Event: ${event.eventName}:${asset.ID}`);
  					// notice how we have access to the transaction information that produced this chaincode event
  					const eventTransaction = event.getTransactionEvent();
  					console.log(`*** transaction: ${eventTransaction.transactionId} status:${eventTransaction.status}`);
  					showTransactionData(eventTransaction.transactionData);
  					// notice how we have access to the full block that contains this transaction
  					const eventBlock = eventTransaction.getBlockEvent();
  					console.log(`*** block: ${eventBlock.blockNumber.toString()}`);
  				};
  				// now start the client side event service and register the listener
  				console.log(`${GREEN}--> Start contract event stream to peer in Org1${RESET}`);
  				await contract1Org1.addContractListener(listener);
  			} catch (eventError) {
  				console.log(`${RED}<-- Failed: Setup contract events - ${eventError}${RESET}`);
  			}
  
  			try {
  				// C R E A T E
  				console.log(`${GREEN}--> Submit Transaction: CreateAsset, ${assetKey} owned by Sam${RESET}`);
  				transaction = contract1Org1.createTransaction('CreateAsset');
  				await transaction.submit(assetKey, 'blue', '10', 'Sam', '100');
  				console.log(`${GREEN}<-- Submit CreateAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (createError) {
  				console.log(`${RED}<-- Submit Failed: CreateAsset - ${createError}${RESET}`);
  			}
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should be owned by Sam${RESET}`);
  				const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '100');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// U P D A T E
  				console.log(`${GREEN}--> Submit Transaction: UpdateAsset ${assetKey} update appraised value to 200`);
  				transaction = contract1Org1.createTransaction('UpdateAsset');
  				await transaction.submit(assetKey, 'blue', '10', 'Sam', '200');
  				console.log(`${GREEN}<-- Submit UpdateAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (updateError) {
  				console.log(`${RED}<-- Failed: UpdateAsset - ${updateError}${RESET}`);
  			}
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now have appraised value of 200${RESET}`);
  				const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '200');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// T R A N S F E R
  				console.log(`${GREEN}--> Submit Transaction: TransferAsset ${assetKey} to Mary`);
  				transaction = contract1Org1.createTransaction('TransferAsset');
  				await transaction.submit(assetKey, 'Mary');
  				console.log(`${GREEN}<-- Submit TransferAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (transferError) {
  				console.log(`${RED}<-- Failed: TransferAsset - ${transferError}${RESET}`);
  			}
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be owned by Mary${RESET}`);
  				const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// D E L E T E
  				console.log(`${GREEN}--> Submit Transaction: DeleteAsset ${assetKey}`);
  				transaction = contract1Org1.createTransaction('DeleteAsset');
  				await transaction.submit(assetKey);
  				console.log(`${GREEN}<-- Submit DeleteAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (deleteError) {
  				console.log(`${RED}<-- Failed: DeleteAsset - ${deleteError}${RESET}`);
  				if (deleteError.toString().includes('ENDORSEMENT_POLICY_FAILURE')) {
  					console.log(`${RED}Be sure that chaincode was deployed with the endorsement policy "OR('Org1MSP.peer','Org2MSP.peer')"${RESET}`);
  				}
  			}
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be deleted${RESET}`);
  				const resultBuffer = await contract1Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
  				console.log(`${RED}<-- Failed: ReadAsset - should not have read this asset${RESET}`);
  			} catch (readError) {
  				console.log(`${GREEN}<-- Success: ReadAsset - ${readError}${RESET}`);
  			}
  
  			// all done with this listener
  			contract1Org1.removeContractListener(listener);
  
  			//
  			//  - - - - - -  B L O C K  E V E N T S  with  P R I V A T E  D A T A
  			//
  			console.log(`${BLUE} **** BLOCK EVENTS with PRIVATE DATA ****${RESET}`);
  			const network2Org1 = await gateway2Org1.getNetwork(channelName);
  			const contract2Org1 = network2Org1.getContract(chaincodeName);
  
  			randomNumber = Math.floor(Math.random() * 1000) + 1;
  			assetKey = `item-${randomNumber}`;
  
  			let firstBlock = true; // simple indicator to track blocks
  
  			try {
  				let listener;
  
  				// create a block listener
  				listener = async (event) => {
  					if (firstBlock) {
  						console.log(`${GREEN}<-- Block Event Received - block number: ${event.blockNumber.toString()}` +
  							'\n### Note:' +
  							'\n    This block event represents the current top block of the ledger.' +
  							`\n    All block events after this one are events that represent new blocks added to the ledger${RESET}`);
  						firstBlock = false;
  					} else {
  						console.log(`${GREEN}<-- Block Event Received - block number: ${event.blockNumber.toString()}${RESET}`);
  					}
  					const transEvents = event.getTransactionEvents();
  					for (const transEvent of transEvents) {
  						console.log(`*** transaction event: ${transEvent.transactionId}`);
  						if (transEvent.privateData) {
  							for (const namespace of transEvent.privateData.ns_pvt_rwset) {
  								console.log(`    - private data: ${namespace.namespace}`);
  								for (const collection of namespace.collection_pvt_rwset) {
  									console.log(`     - collection: ${collection.collection_name}`);
  									if (collection.rwset.reads) {
  										for (const read of collection.rwset.reads) {
  											console.log(`       - read set - ${BLUE}key:${RESET} ${read.key}  ${BLUE}value:${read.value.toString()}`);
  										}
  									}
  									if (collection.rwset.writes) {
  										for (const write of collection.rwset.writes) {
  											console.log(`      - write set - ${BLUE}key:${RESET}${write.key} ${BLUE}is_delete:${RESET}${write.is_delete} ${BLUE}value:${RESET}${write.value.toString()}`);
  										}
  									}
  								}
  							}
  						}
  						if (transEvent.transactionData) {
  							showTransactionData(transEvent.transactionData);
  						}
  					}
  				};
  				// now start the client side event service and register the listener
  				console.log(`${GREEN}--> Start private data block event stream to peer in Org1${RESET}`);
  				await network2Org1.addBlockListener(listener, {type: 'private'});
  			} catch (eventError) {
  				console.log(`${RED}<-- Failed: Setup block events - ${eventError}${RESET}`);
  			}
  
  			try {
  				// C R E A T E
  				console.log(`${GREEN}--> Submit Transaction: CreateAsset, ${assetKey} owned by Sam${RESET}`);
  				transaction = contract2Org1.createTransaction('CreateAsset');
  
  				// create the private data with salt and assign to the transaction
  				const randomNumber = Math.floor(Math.random() * 100) + 1;
  				const asset_properties = {
  					object_type: 'asset_properties',
  					asset_id: assetKey,
  					Price: '90',
  					salt: Buffer.from(randomNumber.toString()).toString('hex')
  				};
  				const asset_properties_string = JSON.stringify(asset_properties);
  				transaction.setTransient({
  					asset_properties: Buffer.from(asset_properties_string)
  				});
  				// With the addition of private data to the transaction
  				// We must only send this to the organization that will be
  				// saving the private data or we will get an endorsement policy failure
  				transaction.setEndorsingOrganizations(org1);
  				// endorse and commit - private data (transient data) will be
  				// saved to the implicit collection on the peer
  				await transaction.submit(assetKey, 'blue', '10', 'Sam', '100');
  				console.log(`${GREEN}<-- Submit CreateAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (createError) {
  				console.log(`${RED}<-- Failed: CreateAsset - ${createError}${RESET}`);
  			}
  			await sleep(5000); // need to wait for event to be committed
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should be owned by Sam${RESET}`);
  				const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '100', '90');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// U P D A T E
  				console.log(`${GREEN}--> Submit Transaction: UpdateAsset ${assetKey} update appraised value to 200`);
  				transaction = contract2Org1.createTransaction('UpdateAsset');
  
  				// update the private data with new salt and assign to the transaction
  				const randomNumber = Math.floor(Math.random() * 100) + 1;
  				const asset_properties = {
  					object_type: 'asset_properties',
  					asset_id: assetKey,
  					Price: '90',
  					salt: Buffer.from(randomNumber.toString()).toString('hex')
  				};
  				const asset_properties_string = JSON.stringify(asset_properties);
  				transaction.setTransient({
  					asset_properties: Buffer.from(asset_properties_string)
  				});
  				transaction.setEndorsingOrganizations(org1);
  
  				await transaction.submit(assetKey, 'blue', '10', 'Sam', '200');
  				console.log(`${GREEN}<-- Submit UpdateAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (updateError) {
  				console.log(`${RED}<-- Failed: UpdateAsset - ${updateError}${RESET}`);
  			}
  			await sleep(5000); // need to wait for event to be committed
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now have appraised value of 200${RESET}`);
  				const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Sam', '200', '90');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// T R A N S F E R
  				console.log(`${GREEN}--> Submit Transaction: TransferAsset ${assetKey} to Mary`);
  				transaction = contract2Org1.createTransaction('TransferAsset');
  
  				// update the private data with new salt and assign to the transaction
  				const randomNumber = Math.floor(Math.random() * 100) + 1;
  				const asset_properties = {
  					object_type: 'asset_properties',
  					asset_id: assetKey,
  					Price: '180',
  					salt: Buffer.from(randomNumber.toString()).toString('hex')
  				};
  				const asset_properties_string = JSON.stringify(asset_properties);
  				transaction.setTransient({
  					asset_properties: Buffer.from(asset_properties_string)
  				});
  				transaction.setEndorsingOrganizations(org1);
  
  				await transaction.submit(assetKey, 'Mary');
  				console.log(`${GREEN}<-- Submit TransferAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (transferError) {
  				console.log(`${RED}<-- Failed: TransferAsset - ${transferError}${RESET}`);
  			}
  			await sleep(5000); // need to wait for event to be committed
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be owned by Mary${RESET}`);
  				const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200', '180');
  			} catch (readError) {
  				console.log(`${RED}<-- Failed: ReadAsset - ${readError}${RESET}`);
  			}
  
  			try {
  				// D E L E T E
  				console.log(`${GREEN}--> Submit Transaction: DeleteAsset ${assetKey}`);
  				transaction = contract2Org1.createTransaction('DeleteAsset');
  				await transaction.submit(assetKey);
  				console.log(`${GREEN}<-- Submit DeleteAsset Result: committed, asset ${assetKey}${RESET}`);
  			} catch (deleteError) {
  				console.log(`${RED}<-- Failed: DeleteAsset - ${deleteError}${RESET}`);
  			}
  			await sleep(5000); // need to wait for event to be committed
  			try {
  				// R E A D
  				console.log(`${GREEN}--> Evaluate: ReadAsset, - ${assetKey} should now be deleted${RESET}`);
  				const resultBuffer = await contract2Org1.evaluateTransaction('ReadAsset', assetKey);
  				checkAsset(org1, resultBuffer, 'blue', '10', 'Mary', '200');
  				console.log(`${RED}<-- Failed: ReadAsset - should not have read this asset${RESET}`);
  			} catch (readError) {
  				console.log(`${GREEN}<-- Success: ReadAsset - ${readError}${RESET}`);
  			}
  
  			// all done with this listener
  			network2Org1.removeBlockListener(listener);
  
  		} catch (runError) {
  			console.error(`Error in transaction: ${runError}`);
  			if (runError.stack) {
  				console.error(runError.stack);
  			}
  		}
  	} catch (error) {
  		console.error(`Error in setup: ${error}`);
  		if (error.stack) {
  			console.error(error.stack);
  		}
  		process.exit(1);
  	}
  
  	await sleep(5000);
  	console.log(`${BLUE} **** END ****${RESET}`);
  	process.exit(0);
  }
  main();
  