nostr-websocket-utils
Version:
Robust WebSocket utilities for Nostr applications with automatic reconnection, supporting both ESM and CommonJS. Features channel-based messaging, heartbeat monitoring, message queueing, and comprehensive error handling with type-safe handlers.
180 lines • 5.61 kB
JavaScript
;
/**
* @file NIP-09: Event Deletion
* @module nips/nip-09
* @see https://github.com/nostr-protocol/nips/blob/master/09.md
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.EVENT_DELETION_KIND = void 0;
exports.createDeletionEvent = createDeletionEvent;
exports.validateDeletionEvent = validateDeletionEvent;
exports.processDeletionEvent = processDeletionEvent;
exports.createEventDeletionManager = createEventDeletionManager;
exports.createDeletionSubscriptionManager = createDeletionSubscriptionManager;
/**
* Event deletion kind
*/
exports.EVENT_DELETION_KIND = 5;
/**
* Creates a deletion event
*/
function createDeletionEvent(eventIds, reason) {
return {
kind: exports.EVENT_DELETION_KIND,
created_at: Math.floor(Date.now() / 1000),
tags: eventIds.map(id => ['e', id]),
content: reason || '',
pubkey: '', // To be filled by the caller
};
}
/**
* Validates a deletion event
*/
async function validateDeletionEvent(event, _logger) {
try {
// Must be kind 5
if (event.kind !== exports.EVENT_DELETION_KIND) {
return false;
}
// Must have at least one e tag
const eventIds = event.tags
.filter((tag) => Array.isArray(tag) && tag[0] === 'e' && typeof tag[1] === 'string')
.map(tag => tag[1]);
if (eventIds.length === 0) {
return false;
}
// Validate each event ID format
const validHexString = /^[0-9a-f]{64}$/;
const invalidIds = eventIds.filter(id => !validHexString.test(id));
if (invalidIds.length > 0) {
return false;
}
return true;
}
catch (error) {
return false;
}
}
/**
* Processes a deletion event
*/
async function processDeletionEvent(event, _logger, deleteEvent) {
try {
if (!validateDeletionEvent(event, _logger)) {
return {
success: false,
error: 'Invalid deletion event'
};
}
const eventIds = event.tags
.filter((tag) => tag[0] === 'e')
.map(tag => tag[1]);
const deletedIds = [];
const failedIds = [];
for (const eventId of eventIds) {
try {
const success = await deleteEvent(eventId);
if (success) {
deletedIds.push(eventId);
}
else {
failedIds.push(eventId);
}
}
catch (error) {
failedIds.push(eventId);
}
}
if (failedIds.length > 0) {
return {
success: false,
error: `Failed to delete events: ${failedIds.join(', ')}`,
deletedIds
};
}
return {
success: true,
deletedIds
};
}
catch (error) {
return {
success: false,
error: 'Unknown error'
};
}
}
/**
* Creates an event deletion manager
* @param _logger - Logger instance
* @returns {EventDeletionManager} Deletion manager
*/
function createEventDeletionManager(_logger) {
// Map of deleted event IDs to deletion reasons
const deletedEvents = new Map();
// Map of deleted event IDs to deletion event IDs
const deletionEvents = new Map();
return {
async processDeletion(message) {
try {
if (!validateDeletionEvent(message, _logger)) {
throw new Error('Invalid deletion event');
}
const eventIds = message.tags
.filter(tag => tag[0] === 'e')
.map(tag => tag[1]);
const { content: reason, id: deletionId } = message;
// Process each event ID
eventIds.forEach(eventId => {
// Only delete if not already deleted or if this deletion is newer
const existingDeletionId = deletionEvents.get(eventId);
if (!existingDeletionId || deletionId > existingDeletionId) {
deletedEvents.set(eventId, reason);
deletionEvents.set(eventId, deletionId);
}
});
return eventIds;
}
catch (error) {
return [];
}
},
isDeleted(eventId) {
return deletedEvents.has(eventId);
},
getDeletionReason(eventId) {
return deletedEvents.get(eventId);
},
getDeletedEvents() {
return new Map(deletedEvents);
}
};
}
/**
* Creates a deletion subscription manager
* @param _logger - Logger instance
* @returns {DeletionSubscriptionManager} Subscription manager
*/
function createDeletionSubscriptionManager(_logger) {
return {
subscribeToDeletions(eventIds) {
return {
subscriptionId: `deletion_${eventIds.join('_')}`,
filters: [{
kinds: [exports.EVENT_DELETION_KIND],
'#e': eventIds
}]
};
},
subscribeToUserDeletions(pubkey) {
return {
subscriptionId: `user_deletion_${pubkey}`,
filters: [{
kinds: [exports.EVENT_DELETION_KIND],
authors: [pubkey]
}]
};
}
};
}
//# sourceMappingURL=nip-09.js.map