UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

15 lines 8.36 kB
{ "name": "metalens-utils-protocol", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/metalens/utils/protocol.ts", "type": "registry:component", "content": "import { PROTOCOL_IDS } from \"../../social/types/protocol.js\";\nimport type { ProtocolDataArray } from \"../../social/types/protocol.js\";\nimport type {\n\tBmapTransactionData,\n\tMetaLensComment,\n\tMetaLensContextType,\n\tPostCommentParams,\n} from \"../types/index.js\";\n\n// Protocol prefixes for MetaLens\nexport const METALENS_PROTOCOL = {\n\tAPP: \"metalens\",\n\tVERSION: \"1.0.0\",\n};\n\n/**\n * Build MAP protocol data for MetaLens comments\n */\nexport function buildMetaLensMapData(params: {\n\tcontext: MetaLensContextType;\n\tvalue: string;\n\tsubcontext?: string;\n\tsubcontextValue?: string;\n\tparentId?: string;\n\ttags?: string[];\n\tmentions?: string[];\n}) {\n\tconst mapData: Record<string, string> = {\n\t\tapp: METALENS_PROTOCOL.APP,\n\t\ttype: params.parentId ? \"reply\" : \"comment\",\n\t\tcontext: params.context,\n\t\t[params.context]: params.value,\n\t\ttimestamp: new Date().toISOString(),\n\t};\n\n\t// Add subcontext if provided\n\tif (params.subcontext && params.subcontextValue) {\n\t\tmapData.subcontext = params.subcontext;\n\t\tmapData[params.subcontext] = params.subcontextValue;\n\t}\n\n\t// Add parent reference for replies\n\tif (params.parentId) {\n\t\tmapData.thread = params.parentId;\n\t}\n\n\t// Add tags if provided\n\tif (params.tags && params.tags.length > 0) {\n\t\tmapData.tags = params.tags.join(\",\");\n\t}\n\n\t// Add mentions if provided\n\tif (params.mentions && params.mentions.length > 0) {\n\t\tmapData.mentions = params.mentions.join(\",\");\n\t}\n\n\treturn mapData;\n}\n\n/**\n * Build a complete MetaLens comment transaction\n */\nexport function buildMetaLensTransaction(\n\tparams: PostCommentParams,\n): ProtocolDataArray {\n\tconst mapData = buildMetaLensMapData(params);\n\n\t// Build protocol data array\n\t// Format: [B protocol data, MAP protocol data, AIP will be added by signing]\n\tconst protocolData: ProtocolDataArray = [];\n\n\t// B protocol for content\n\tprotocolData.push(\n\t\tPROTOCOL_IDS.B, // B prefix\n\t\tparams.content,\n\t\tparams.contentType || \"text/plain\",\n\t\t\"utf8\",\n\t);\n\n\t// MAP protocol for metadata\n\tprotocolData.push(\n\t\tPROTOCOL_IDS.MAP, // MAP prefix\n\t\t\"SET\",\n\t);\n\n\t// Add all MAP data as key-value pairs\n\tfor (const [key, value] of Object.entries(mapData)) {\n\t\tprotocolData.push(key, value);\n\t}\n\n\treturn protocolData;\n}\n\n/**\n * Parse MetaLens comment from transaction data\n */\nexport function parseMetaLensComment(\n\tdata: BmapTransactionData,\n): MetaLensComment | null {\n\ttry {\n\t\t// Extract MAP data\n\t\tconst mapData = data.MAP?.[0] || {};\n\n\t\t// Extract content from B protocol\n\t\tconst content = data.B?.[0]?.content || \"\";\n\t\tconst contentType = data.B?.[0]?.[\"content-type\"] || \"text/plain\";\n\n\t\t// Extract author from AIP (for properly signed transactions)\n\t\tconst aipData = data.AIP?.[0] || {};\n\t\tconst author = (aipData as { address?: string }).address || \"Anonymous\";\n\n\t\t// For now, accept all social posts as potential MetaLens comments\n\t\t// Later we'll filter for proper MetaLens protocol data\n\t\tconst isMetaLensComment = mapData.app === METALENS_PROTOCOL.APP;\n\n\t\t// If it's a proper MetaLens comment, parse the context\n\t\tlet contextType: MetaLensContextType = \"custom\";\n\t\tlet contextValue = \"\";\n\n\t\tif (isMetaLensComment) {\n\t\t\tcontextType = mapData.context as MetaLensContextType;\n\t\t\tcontextValue = mapData[contextType] || \"\";\n\t\t} else {\n\t\t\t// For legacy posts, try to infer context from content\n\t\t\t// This is a fallback until we have proper MetaLens protocol adoption\n\t\t\tif (content.includes(\"http://\") || content.includes(\"https://\")) {\n\t\t\t\tcontextType = \"url\";\n\t\t\t\tconst urlMatch = content.match(/(https?:\\/\\/[^\\s]+)/);\n\t\t\t\tcontextValue = urlMatch?.[1] || content;\n\t\t\t} else {\n\t\t\t\tcontextType = \"custom\";\n\t\t\t\tcontextValue = content.substring(0, 50); // Use first 50 chars as context\n\t\t\t}\n\t\t}\n\n\t\t// Parse timestamp\n\t\tconst timestamp = mapData.timestamp\n\t\t\t? new Date(mapData.timestamp)\n\t\t\t: new Date(data.timestamp || Date.now());\n\n\t\t// Build comment object\n\t\tconst comment: MetaLensComment = {\n\t\t\ttxid: data.tx?.h || \"\",\n\t\t\tauthor,\n\t\t\tcontent,\n\t\t\tcontentType: contentType as \"text/plain\" | \"text/markdown\",\n\t\t\ttimestamp,\n\t\t\tcontext: {\n\t\t\t\ttype: contextType,\n\t\t\t\tvalue: contextValue,\n\t\t\t\tsubcontext: mapData.subcontext,\n\t\t\t\tsubcontextValue: mapData.subcontext\n\t\t\t\t\t? mapData[mapData.subcontext]\n\t\t\t\t\t: undefined,\n\t\t\t},\n\t\t\tmetadata: {\n\t\t\t\tapp: mapData.app || \"unknown\",\n\t\t\t\tthread: mapData.thread,\n\t\t\t\ttags: mapData.tags?.split(\",\").filter(Boolean),\n\t\t\t\tmentions: mapData.mentions?.split(\",\").filter(Boolean),\n\t\t\t},\n\t\t};\n\n\t\treturn comment;\n\t} catch (error) {\n\t\tconsole.error(\"Failed to parse MetaLens comment:\", error);\n\t\treturn null;\n\t}\n}\n\n/**\n * Build EventSource URL for real-time updates\n */\nexport function buildMetaLensStreamUrl(\n\tapiUrl: string,\n\tcontext: MetaLensContextType,\n\tvalue: string,\n): string {\n\tconst params = new URLSearchParams({\n\t\tcontext,\n\t\tvalue,\n\t\tapp: METALENS_PROTOCOL.APP,\n\t});\n\n\treturn `${apiUrl}/stream/comments?${params.toString()}`;\n}\n\n/**\n * Validate context type and value\n */\nexport function validateContext(\n\ttype: MetaLensContextType,\n\tvalue: string,\n): { valid: boolean; error?: string } {\n\tif (!value || typeof value !== \"string\") {\n\t\treturn { valid: false, error: \"Context value is required\" };\n\t}\n\n\tswitch (type) {\n\t\tcase \"tx\":\n\t\t\t// Validate transaction ID (64 hex chars)\n\t\t\tif (!/^[a-fA-F0-9]{64}$/.test(value)) {\n\t\t\t\treturn { valid: false, error: \"Invalid transaction ID format\" };\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase \"upc\":\n\t\t\t// Validate UPC (12-14 digits)\n\t\t\tif (!/^\\d{12,14}$/.test(value)) {\n\t\t\t\treturn { valid: false, error: \"Invalid UPC format\" };\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase \"url\":\n\t\t\t// Validate URL\n\t\t\ttry {\n\t\t\t\tnew URL(value);\n\t\t\t} catch {\n\t\t\t\treturn { valid: false, error: \"Invalid URL format\" };\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase \"geohash\":\n\t\t\t// Basic geohash validation (alphanumeric, 4-12 chars)\n\t\t\tif (!/^[0-9a-z]{4,12}$/.test(value)) {\n\t\t\t\treturn { valid: false, error: \"Invalid geohash format\" };\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase \"ipfs\":\n\t\t\t// IPFS hash validation\n\t\t\tif (!value.startsWith(\"Qm\") && !value.startsWith(\"ipfs://\")) {\n\t\t\t\treturn { valid: false, error: \"Invalid IPFS hash format\" };\n\t\t\t}\n\t\t\tbreak;\n\n\t\tcase \"isbn\": {\n\t\t\t// ISBN validation (10 or 13 digits)\n\t\t\tconst cleanIsbn = value.replace(/-/g, \"\");\n\t\t\tif (!/^(?:\\d{9}[\\dX]|\\d{13})$/.test(cleanIsbn)) {\n\t\t\t\treturn { valid: false, error: \"Invalid ISBN format\" };\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"doi\":\n\t\t\t// DOI validation\n\t\t\tif (!/^10\\.\\d{4,}\\/[-._;()\\/:a-zA-Z0-9]+$/.test(value)) {\n\t\t\t\treturn { valid: false, error: \"Invalid DOI format\" };\n\t\t\t}\n\t\t\tbreak;\n\t}\n\n\treturn { valid: true };\n}\n\n/**\n * Format context for display\n */\nexport function formatContextDisplay(\n\ttype: MetaLensContextType,\n\tvalue: string,\n): string {\n\tswitch (type) {\n\t\tcase \"url\":\n\t\t\ttry {\n\t\t\t\tconst url = new URL(value);\n\t\t\t\treturn url.hostname + (url.pathname !== \"/\" ? url.pathname : \"\");\n\t\t\t} catch {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\tcase \"tx\":\n\t\t\treturn `${value.substring(0, 8)}...${value.substring(56)}`;\n\n\t\tcase \"upc\":\n\t\t\treturn `Product ${value}`;\n\n\t\tcase \"geohash\":\n\t\t\treturn `Location ${value.substring(0, 6)}`;\n\n\t\tcase \"isbn\":\n\t\t\treturn `Book ISBN ${value}`;\n\n\t\tcase \"doi\":\n\t\t\treturn `Paper ${value}`;\n\n\t\tcase \"ipfs\":\n\t\t\treturn `IPFS ${value.substring(0, 12)}...`;\n\n\t\tdefault:\n\t\t\treturn value;\n\t}\n}\n", "target": "<%- config.aliases.components %>/protocol.tsx" } ] }