import { Tx } from '@cmdcode/tapscript'; import { readEnvelope } from './inscription.js'; // Rebuild solely from serialized transactions, never collectible/UI sidecars. // Discovery is a convention, not native ord metaprotocol tag 7 support. export class MetadataIndex { constructor(chain) { this.events = []; const visit = (raw, height, block) => { const tx = Tx.decode(raw); if (tx.vin[0]?.witness?.length !== 3) return; const item = readEnvelope(tx.vin[0].witness); const metadata = item.metadata; this.events.push({ id: `${Tx.util.getTxid(tx)}i0`, contentType: item.contentType, status: height === null ? 'pending' : 'confirmed', height, block, metadata: metadata ?? null, hasMetadata: metadata !== undefined, parent: item.parent, protocol: metadata && !Array.isArray(metadata) && typeof metadata === 'object' && Object.hasOwn(metadata, 'protocol') && typeof metadata.protocol === 'string' ? metadata.protocol : null }); }; for (const block of chain.blocks) for (const raw of block.raws) visit(raw, block.height, block.hash); for (const raw of chain.mempool) visit(raw, null, null); } forProtocol(name) { return this.events.filter(e => e.protocol === name); } get protocols() { return [...new Set(this.events.filter(e => e.protocol !== null).map(e => e.protocol))].sort(); } get stats() { return { events: this.events.length, withMetadata: this.events.filter(e => e.hasMetadata).length, confirmed: this.events.filter(e => e.status === 'confirmed').length, pending: this.events.filter(e => e.status === 'pending').length, protocols: this.protocols }; } } // Trusted, application-bundled reducers only. Adding a plugin requires a code change/review. // No eval, dynamic imports, inscription scripts, or interpretation in MetadataIndex. const reducers = Object.freeze({ 'event-counter': events => events.reduce((state, event) => ({ count: state.count + 1, confirmed: state.confirmed + Number(event.status === 'confirmed'), pending: state.pending + Number(event.status === 'pending'), ids: [...state.ids, event.id], }), { count: 0, confirmed: 0, pending: 0, ids: [] }), }); export function deriveProtocol(index, protocol, reducerName) { if (!Object.hasOwn(reducers, reducerName)) throw new Error('Unknown trusted local reducer.'); return reducers[reducerName](structuredClone(index.forProtocol(protocol))); }