UNPKG

@inkwell.ar/sdk

Version:

SDK for interacting with the Inkwell Blog CRUD AO process using aoconnect for deployment and interactions

747 lines 34.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InkwellBlogSDK = void 0; const aoconnect_1 = require("@permaweb/aoconnect"); const logger_1 = require("../utils/logger"); const browser_deploy_1 = require("./browser-deploy"); const validation_1 = require("../utils/validation"); class InkwellBlogSDK { constructor(config) { (0, validation_1.validateSDKConfig)(config); this.processId = config.processId; this.aoconnect = config.aoconnect || (0, aoconnect_1.connect)({ MODE: 'legacy' }); this.logger = new logger_1.Logger({ level: config.logLevel || logger_1.LogLevel.WARN }); this.logger.info(logger_1.LogGroup.SDK, `Initialized InkwellBlogSDK with process ID: ${this.processId}`); } /** * Get the appropriate signer for the wallet */ getSigner(wallet) { if (wallet) { this.logger.debug(logger_1.LogGroup.AUTH, 'Using provided wallet for signing'); return (0, aoconnect_1.createDataItemSigner)(wallet); } // Check for browser wallet if (typeof globalThis !== 'undefined' && globalThis.arweaveWallet) { this.logger.debug(logger_1.LogGroup.AUTH, 'Using browser wallet for signing'); return (0, aoconnect_1.createDataItemSigner)(globalThis.arweaveWallet); } this.logger.error(logger_1.LogGroup.AUTH, 'No wallet available for signing - neither provided nor browser wallet found'); throw new Error('No wallet provided and no browser wallet available. Please provide a wallet or connect a browser wallet.'); } /** * Get the result of a message using its message ID */ async getMessageResult(messageId) { try { this.logger.debug(logger_1.LogGroup.API, `Getting message result for ID: ${messageId}`); const resultData = await this.aoconnect.result({ message: messageId, process: this.processId, }); this.logger.debug(logger_1.LogGroup.API, `Successfully retrieved message result`); return resultData; } catch (error) { this.logger.error(logger_1.LogGroup.API, `Failed to get message result for ${messageId}`, error); throw new Error(`Failed to get message result: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Deploy a new Inkwell Blog process * Works in both browser and Node.js environments using aoconnect */ static async deploy(options = {}) { const logLevel = options.logLevel || logger_1.LogLevel.INFO; const logger = new logger_1.Logger({ level: logLevel }); try { logger.info(logger_1.LogGroup.DEPLOY, `Starting blog deployment with name: ${options.name || 'unnamed'}`); // Use browser deployment for all environ ments const result = await (0, browser_deploy_1.deployBlogInBrowser)({ name: options.name, wallet: options.wallet, aoconnect: options.aoconnect, logLevel: logLevel, onBoot: options.onBoot, pollForSpawn: options.pollForSpawn, }); logger.info(logger_1.LogGroup.DEPLOY, `Blog deployed successfully with process ID: ${result.processId}`); return result; } catch (error) { logger.error(logger_1.LogGroup.DEPLOY, `Failed to deploy blog process`, error); throw new Error(`Failed to deploy Inkwell Blog process: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Get blog information */ async getInfo() { try { this.logger.debug(logger_1.LogGroup.API, 'Getting blog info'); const result = await this.aoconnect.dryrun({ process: this.processId, tags: [{ name: 'Action', value: 'Info' }], }); const response = await this.parseInfoResponse(result); this.logger.debug(logger_1.LogGroup.API, 'Blog info retrieved successfully'); return response; } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to get blog info', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Get all posts from the blog */ async getAllPosts(options = {}) { try { this.logger.debug(logger_1.LogGroup.API, `Getting all posts with options: ${JSON.stringify(options)}`); const tags = [{ name: 'Action', value: 'Get-All-Posts' }]; if (options.ordered !== undefined) { tags.push({ name: 'Ordered', value: options.ordered.toString() }); } const result = await this.aoconnect.dryrun({ process: this.processId, tags, }); this.logger.debug(logger_1.LogGroup.API, `Result: ${JSON.stringify(result)}`); const response = await this.parseDryrunResponse(result, true); if (response.success && Array.isArray(response.data)) { this.logger.info(logger_1.LogGroup.API, `Retrieved ${response.data.length} posts`); } return response; } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to get all posts', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Get a specific post by ID */ async getPost(options) { try { this.logger.debug(logger_1.LogGroup.API, `Getting post with ID: ${options.id}`); (0, validation_1.validatePostId)(options.id); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Post ID validation passed'); const result = await this.aoconnect.dryrun({ process: this.processId, tags: [ { name: 'Action', value: 'Get-Post' }, { name: 'Id', value: options.id.toString() }, ], }); const response = await this.parseDryrunResponse(result); if (response.success) { this.logger.debug(logger_1.LogGroup.API, `Successfully retrieved post ${options.id}`); } return response; } catch (error) { this.logger.error(logger_1.LogGroup.API, `Failed to get post ${options.id}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Get user roles for the current wallet */ async getUserRoles(walletAddress) { try { this.logger.debug(logger_1.LogGroup.AUTH, `Getting roles for wallet: ${walletAddress}`); const result = await this.aoconnect.dryrun({ process: this.processId, tags: [ { name: 'Action', value: 'Get-User-Roles' }, { name: 'User-Address', value: walletAddress }, ], }); const response = await this.parseDryrunResponse(result, true); if (response.success && Array.isArray(response.data)) { this.logger.info(logger_1.LogGroup.AUTH, `User has ${response.data.length} roles: ${response.data.join(', ')}`); } return response; } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, `Failed to get user roles for ${walletAddress}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Create a new post (Editor role required) */ async createPost(options) { try { this.logger.info(logger_1.LogGroup.API, `Creating post: ${options.data.title}`); (0, validation_1.validateCreatePostData)(options.data); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Post data validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Create-Post' }], data: JSON.stringify(options.data), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.API, 'Create-Post message failed - no message ID returned'); return { success: false, data: 'Create-Post message failed', }; } this.logger.debug(logger_1.LogGroup.API, `Post creation message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.API, 'Post created successfully'); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.API, `Could not retrieve post creation result, but message was sent: ${messageId}`); // If we can't get the result, return a success message with the message ID return { success: true, data: `Post created successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to create post', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Update an existing post (Editor role required) */ async updatePost(options) { try { this.logger.info(logger_1.LogGroup.API, `Updating post ${options.id}: ${options.data.title}`); (0, validation_1.validatePostId)(options.id); (0, validation_1.validateUpdatePostData)(options.data); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Post update data validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [ { name: 'Action', value: 'Update-Post' }, { name: 'Id', value: options.id.toString() }, ], data: JSON.stringify(options.data), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.API, 'Update-Post message failed - no message ID returned'); return { success: false, data: 'Update-Post message failed', }; } this.logger.debug(logger_1.LogGroup.API, `Post update message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.API, `Post ${options.id} updated successfully`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.API, `Could not retrieve post update result, but message was sent: ${messageId}`); return { success: true, data: `Post updated successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.API, `Failed to update post ${options.id}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Delete a post (Editor role required) */ async deletePost(options) { try { this.logger.info(logger_1.LogGroup.API, `Deleting post ${options.id}`); (0, validation_1.validatePostId)(options.id); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Post ID validation passed for deletion'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [ { name: 'Action', value: 'Delete-Post' }, { name: 'Id', value: options.id.toString() }, ], signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.API, 'Delete-Post message failed - no message ID returned'); return { success: false, data: 'Delete-Post message failed', }; } this.logger.debug(logger_1.LogGroup.API, `Post deletion message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.API, `Post ${options.id} deleted successfully`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.API, `Could not retrieve post deletion result, but message was sent: ${messageId}`); return { success: true, data: `Post ${options.id} deleted successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.API, `Failed to delete post ${options.id}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Add editors to the blog (Admin role required) */ async addEditors(options) { try { this.logger.info(logger_1.LogGroup.AUTH, `Adding editors: ${options.accounts.join(', ')}`); (0, validation_1.validateRoleManagementOptions)(options); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Role management options validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Add-Editors' }], data: JSON.stringify({ accounts: options.accounts }), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.AUTH, 'Add-Editors message failed - no message ID returned'); return { success: false, data: 'Add-Editors message failed', }; } this.logger.debug(logger_1.LogGroup.AUTH, `Add editors message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.AUTH, `Successfully added ${options.accounts.length} editors`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.AUTH, `Could not retrieve add editors result, but message was sent: ${messageId}`); return { success: true, data: `Editors ${options.accounts.join(', ')} added successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, `Failed to add editors: ${options.accounts.join(', ')}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Remove editors from the blog (Admin role required) */ async removeEditors(options) { try { this.logger.info(logger_1.LogGroup.AUTH, `Removing editors: ${options.accounts.join(', ')}`); (0, validation_1.validateRoleManagementOptions)(options); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Role management options validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Remove-Editors' }], data: JSON.stringify({ accounts: options.accounts }), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.AUTH, 'Remove-Editors message failed - no message ID returned'); return { success: false, data: 'Remove-Editors message failed', }; } this.logger.debug(logger_1.LogGroup.AUTH, `Remove editors message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.AUTH, `Successfully removed ${options.accounts.length} editors`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.AUTH, `Could not retrieve remove editors result, but message was sent: ${messageId}`); return { success: true, data: `Editors ${options.accounts.join(', ')} removed successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, `Failed to remove editors: ${options.accounts.join(', ')}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Add admins to the blog (Admin role required) */ async addAdmins(options) { try { this.logger.info(logger_1.LogGroup.AUTH, `Adding admins: ${options.accounts.join(', ')}`); (0, validation_1.validateRoleManagementOptions)(options); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Role management options validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Add-Admins' }], data: JSON.stringify({ accounts: options.accounts }), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.AUTH, 'Add-Admins message failed - no message ID returned'); return { success: false, data: 'Add-Admins message failed', }; } this.logger.debug(logger_1.LogGroup.AUTH, `Add admins message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.AUTH, `Successfully added ${options.accounts.length} admins`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.AUTH, `Could not retrieve add admins result, but message was sent: ${messageId}`); return { success: true, data: `Admins ${options.accounts.join(', ')} added successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, `Failed to add admins: ${options.accounts.join(', ')}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Remove admins from the blog (Admin role required) */ async removeAdmins(options) { try { this.logger.info(logger_1.LogGroup.AUTH, `Removing admins: ${options.accounts.join(', ')}`); (0, validation_1.validateRoleManagementOptions)(options); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Role management options validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Remove-Admins' }], data: JSON.stringify({ accounts: options.accounts }), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.AUTH, 'Remove-Admins message failed - no message ID returned'); return { success: false, data: 'Remove-Admins message failed', }; } this.logger.debug(logger_1.LogGroup.AUTH, `Remove admins message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.AUTH, `Successfully removed ${options.accounts.length} admins`); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.AUTH, `Could not retrieve remove admins result, but message was sent: ${messageId}`); return { success: true, data: `Admins ${options.accounts.join(', ')} removed successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, `Failed to remove admins: ${options.accounts.join(', ')}`, error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Get all editors (Admin role required) */ async getEditors() { try { this.logger.debug(logger_1.LogGroup.AUTH, 'Getting all editors'); const result = await this.aoconnect.dryrun({ process: this.processId, tags: [{ name: 'Action', value: 'Get-Editors' }], }); const response = await this.parseDryrunResponse(result, true); if (response.success && Array.isArray(response.data)) { this.logger.info(logger_1.LogGroup.AUTH, `Retrieved ${response.data.length} editors`); } return response; } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, 'Failed to get editors', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Get all admins (Admin role required) */ async getAdmins() { try { this.logger.debug(logger_1.LogGroup.AUTH, 'Getting all admins'); const result = await this.aoconnect.dryrun({ process: this.processId, tags: [{ name: 'Action', value: 'Get-Admins' }], }); const response = await this.parseDryrunResponse(result, true); if (response.success && Array.isArray(response.data)) { this.logger.info(logger_1.LogGroup.AUTH, `Retrieved ${response.data.length} admins`); } return response; } catch (error) { this.logger.error(logger_1.LogGroup.AUTH, 'Failed to get admins', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Set blog details (Admin role required) */ async setBlogDetails(options) { try { this.logger.info(logger_1.LogGroup.API, `Updating blog details: ${JSON.stringify(options.data)}`); (0, validation_1.validateBlogDetailsData)(options.data); this.logger.debug(logger_1.LogGroup.VALIDATION, 'Blog details validation passed'); const messageId = await this.aoconnect.message({ process: this.processId, tags: [{ name: 'Action', value: 'Set-Blog-Details' }], data: JSON.stringify(options.data), signer: this.getSigner(options.wallet), }); if (!messageId) { this.logger.error(logger_1.LogGroup.API, 'Set-Blog-Details message failed - no message ID returned'); return { success: false, data: 'Set-Blog-Details message failed', }; } this.logger.debug(logger_1.LogGroup.API, `Blog details update message sent with ID: ${messageId}`); // Try to get the result of the message try { const resultData = await this.getMessageResult(messageId); const response = await this.parseMessageResponse(resultData, options.wallet); this.logger.info(logger_1.LogGroup.API, 'Blog details updated successfully'); return response; } catch (resultError) { this.logger.warn(logger_1.LogGroup.API, `Could not retrieve blog details update result, but message was sent: ${messageId}`); return { success: true, data: `Blog details updated successfully. Message ID: ${messageId}`, }; } } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to update blog details', error); return { success: false, data: error instanceof Error ? error.message : 'Unknown error occurred', }; } } /** * Parse the response */ async parseResponse(options) { const { result, isDryrun = false, recursiveParse = false, optionsWallet, } = options; try { this.logger.debug(logger_1.LogGroup.API, `Parsing ${isDryrun ? 'dryrun' : 'message'} response with ${result?.Messages?.length || 0} messages`); if (result && result.Messages && result.Messages.length > 0) { let message; if (isDryrun) { // For dryrun operations, always use the first message message = result.Messages[0]; this.logger.debug(logger_1.LogGroup.API, 'Using first message for dryrun operation'); } else { // For message operations, try to find the message targeted to the wallet const wallet = optionsWallet || (await globalThis.arweaveWallet.getActiveAddress()); if (!wallet) { message = result.Messages[0]; this.logger.debug(logger_1.LogGroup.API, 'Using first message (no wallet specified)'); } else { message = result.Messages.find((m) => m.Tags?.Target === wallet || m.Target === wallet); if (!message) { // Fallback to first message if no targeted message found message = result.Messages[0]; this.logger.debug(logger_1.LogGroup.API, `No message found for wallet ${wallet}, using first message`); } else { this.logger.debug(logger_1.LogGroup.API, `Found targeted message for wallet: ${wallet}`); } } } const data = message?.Data; if (data) { this.logger.debug(logger_1.LogGroup.API, 'Parsing message data as JSON: ', data); const parsed = JSON.parse(data); if (recursiveParse && typeof parsed.data === 'string') { this.logger.debug(logger_1.LogGroup.API, 'Performing recursive JSON parse on data field: ', parsed.data); parsed.data = JSON.parse(parsed.data); } this.logger.debug(logger_1.LogGroup.API, `Response parsed successfully: ${parsed.success ? 'success' : 'failure'}`); return { success: parsed.success, data: parsed.data || parsed, }; } } this.logger.warn(logger_1.LogGroup.API, 'Invalid response format from process - no valid messages found'); return { success: false, data: 'Invalid response format from process', }; } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to parse response JSON', error); return { success: false, data: error instanceof Error ? error.message : 'Failed to parse response', }; } } /** * Parse the response from dryrun operations */ async parseDryrunResponse(result, recursiveParse = false) { return await this.parseResponse({ result, isDryrun: true, recursiveParse, }); } /** * Parse the response from message result operations */ async parseMessageResponse(result, optionsWallet, recursiveParse = true) { return await this.parseResponse({ result, isDryrun: false, recursiveParse, optionsWallet, }); } /** * Parse the Info response from the AO process */ async parseInfoResponse(result) { try { this.logger.debug(logger_1.LogGroup.API, 'Parsing blog info response'); if (result && result.Messages && result.Messages.length > 0) { const message = result.Messages[0]; this.logger.debug(logger_1.LogGroup.API, 'Extracting blog info from message tags'); // Info handler returns data in message tags and Data field const info = { name: message.Tags?.Name || '', author: message.Tags?.Author || '', blogTitle: message.Tags?.['Blog-Title'] || '', blogDescription: message.Tags?.['Blog-Description'] || '', blogLogo: message.Tags?.['Blog-Logo'] || '', details: { title: message.Tags?.['Blog-Title'] || '', description: message.Tags?.['Blog-Description'] || '', logo: message.Tags?.['Blog-Logo'] || '', }, }; // Also try to parse the Data field for additional details if (message.Data) { try { this.logger.debug(logger_1.LogGroup.API, 'Parsing additional blog info from Data field'); const parsedData = JSON.parse(message.Data); if (parsedData.success && parsedData.data) { this.logger.debug(logger_1.LogGroup.API, 'Merging parsed data with tag-based info'); info.details = { title: parsedData.data.title || info.details.title, description: parsedData.data.description || info.details.description, logo: parsedData.data.logo || info.details.logo, }; } } catch (parseError) { this.logger.warn(logger_1.LogGroup.API, 'Failed to parse Data field, using tag-based info only', parseError); } } this.logger.debug(logger_1.LogGroup.API, `Blog info parsed successfully: ${info.name}`); return { success: true, data: info, }; } this.logger.warn(logger_1.LogGroup.API, 'Invalid blog info response format - no messages found'); return { success: false, data: 'Invalid response format from process', }; } catch (error) { this.logger.error(logger_1.LogGroup.API, 'Failed to parse blog info response', error); return { success: false, data: error instanceof Error ? error.message : 'Failed to parse Info response', }; } } } exports.InkwellBlogSDK = InkwellBlogSDK; //# sourceMappingURL=blog-sdk.js.map