@ -1,10 +1,6 @@
import { getMimeTags } from "../utils/mime.ts" ;
import { getMimeTags } from "../utils/mime.ts" ;
import {
import { metadataToTags } from "../utils/asciidoc_metadata.ts" ;
metadataToTags ,
import { parseAsciiDocWithMetadata } from "../utils/asciidoc_parser.ts" ;
} from "../utils/asciidoc_metadata.ts" ;
import {
parseAsciiDocWithMetadata ,
} from "../utils/asciidoc_parser.ts" ;
import NDK , { NDKEvent , NDKRelaySet } from "@nostr-dev-kit/ndk" ;
import NDK , { NDKEvent , NDKRelaySet } from "@nostr-dev-kit/ndk" ;
import { nip19 } from "nostr-tools" ;
import { nip19 } from "nostr-tools" ;
@ -14,6 +10,14 @@ export interface PublishResult {
error? : string ;
error? : string ;
}
}
export interface ProcessedPublishResults {
successCount : number ;
total : number ;
errors : string [ ] ;
successfulEvents : Array < { eventId : string ; title : string } > ;
failedEvents : Array < { title : string ; error : string ; sectionIndex : number } > ;
}
export interface PublishOptions {
export interface PublishOptions {
content : string ;
content : string ;
kind? : number ;
kind? : number ;
@ -98,9 +102,8 @@ export async function publishZettel(
throw new Error ( "Failed to publish to any relays" ) ;
throw new Error ( "Failed to publish to any relays" ) ;
}
}
} catch ( error ) {
} catch ( error ) {
const errorMessage = error instanceof Error
const errorMessage =
? error . message
error instanceof Error ? error . message : "Unknown error" ;
: "Unknown error" ;
onError ? . ( errorMessage ) ;
onError ? . ( errorMessage ) ;
return { success : false , error : errorMessage } ;
return { success : false , error : errorMessage } ;
}
}
@ -122,43 +125,56 @@ export async function publishSingleEvent(
) : Promise < PublishResult > {
) : Promise < PublishResult > {
const { content , kind , tags , onError } = options ;
const { content , kind , tags , onError } = options ;
if ( ! ndk ? . activeUser ) {
if ( ! ndk ? . activeUser ) {
const error = 'Please log in first' ;
const error = "Please log in first" ;
onError ? . ( error ) ;
onError ? . ( error ) ;
return { success : false , error } ;
return { success : false , error } ;
}
}
try {
try {
const allRelayUrls = Array . from ( ndk . pool ? . relays . values ( ) || [ ] ) . map ( ( r ) = > r . url ) ;
const allRelayUrls = Array . from ( ndk . pool ? . relays . values ( ) || [ ] ) . map (
( r ) = > r . url ,
) ;
if ( allRelayUrls . length === 0 ) {
if ( allRelayUrls . length === 0 ) {
throw new Error ( 'No relays available in NDK pool' ) ;
throw new Error ( "No relays available in NDK pool" ) ;
}
}
const relaySet = NDKRelaySet . fromRelayUrls ( allRelayUrls , ndk ) ;
const relaySet = NDKRelaySet . fromRelayUrls ( allRelayUrls , ndk ) ;
// Fix a-tags that have placeholder "pubkey" with actual pubkey
// Fix a-tags that have placeholder "pubkey" with actual pubkey
const fixedTags = tags . map ( tag = > {
const fixedTags = tags . map ( ( tag ) = > {
if ( tag [ 0 ] === 'a' && tag [ 1 ] && tag [ 1 ] . includes ( ':pubkey:' ) && ndk . activeUser ) {
if (
tag [ 0 ] === "a" &&
tag [ 1 ] &&
tag [ 1 ] . includes ( ":pubkey:" ) &&
ndk . activeUser
) {
// Replace "pubkey" placeholder with actual pubkey
// Replace "pubkey" placeholder with actual pubkey
const fixedATag = tag [ 1 ] . replace ( ':pubkey:' , ` : ${ ndk . activeUser . pubkey } : ` ) ;
const fixedATag = tag [ 1 ] . replace (
return [ tag [ 0 ] , fixedATag , tag [ 2 ] || '' , tag [ 3 ] || '' ] ;
":pubkey:" ,
` : ${ ndk . activeUser . pubkey } : ` ,
) ;
return [ tag [ 0 ] , fixedATag , tag [ 2 ] || "" , tag [ 3 ] || "" ] ;
}
}
return tag ;
return tag ;
} ) ;
} ) ;
// Auto-add author identity if not publishing on behalf of others
// Auto-add author identity if not publishing on behalf of others
const hasAuthorTag = fixedTags . some ( tag = > tag [ 0 ] === 'author' ) ;
const hasAuthorTag = fixedTags . some ( ( tag ) = > tag [ 0 ] === "author" ) ;
const hasPTag = fixedTags . some ( tag = > tag [ 0 ] === 'p' ) ;
const hasPTag = fixedTags . some ( ( tag ) = > tag [ 0 ] === "p" ) ;
const finalTags = [ . . . fixedTags ] ;
const finalTags = [ . . . fixedTags ] ;
if ( ! hasAuthorTag && ndk . activeUser ) {
if ( ! hasAuthorTag && ndk . activeUser ) {
// Add display name as author
// Add display name as author
const displayName = ndk . activeUser . profile ? . displayName || ndk . activeUser . profile ? . name || 'Anonymous' ;
const displayName =
finalTags . push ( [ 'author' , displayName ] ) ;
ndk . activeUser . profile ? . displayName ||
ndk . activeUser . profile ? . name ||
"Anonymous" ;
finalTags . push ( [ "author" , displayName ] ) ;
}
}
if ( ! hasPTag && ndk . activeUser ) {
if ( ! hasPTag && ndk . activeUser ) {
// Add pubkey as p-tag
// Add pubkey as p-tag
finalTags . push ( [ 'p' , ndk . activeUser . pubkey ] ) ;
finalTags . push ( [ "p" , ndk . activeUser . pubkey ] ) ;
}
}
// Create and sign NDK event
// Create and sign NDK event
@ -176,22 +192,25 @@ export async function publishSingleEvent(
if ( publishedToRelays . size > 0 ) {
if ( publishedToRelays . size > 0 ) {
// Debug: Log the event structure in a clean, concise format
// Debug: Log the event structure in a clean, concise format
const dTagEntry = tags . find ( t = > t [ 0 ] === 'd' ) ;
const dTagEntry = tags . find ( ( t ) = > t [ 0 ] === "d" ) ;
const dTag = dTagEntry ? dTagEntry [ 1 ] : '' ;
const dTag = dTagEntry ? dTagEntry [ 1 ] : "" ;
const titleTag = tags . find ( t = > t [ 0 ] === 'title' ) ;
const titleTag = tags . find ( ( t ) = > t [ 0 ] === "title" ) ;
const title = titleTag ? titleTag [ 1 ] : 'Untitled' ;
const title = titleTag ? titleTag [ 1 ] : "Untitled" ;
console . log ( ` Event verified: ${ ndkEvent . id } ` ) ;
console . log ( ` Event verified: ${ ndkEvent . id } ` ) ;
return { success : true , eventId : ndkEvent.id } ;
return { success : true , eventId : ndkEvent.id } ;
} else {
} else {
const titleTag = tags . find ( t = > t [ 0 ] === 'title' ) ;
const titleTag = tags . find ( ( t ) = > t [ 0 ] === "title" ) ;
const title = titleTag ? titleTag [ 1 ] : 'Untitled' ;
const title = titleTag ? titleTag [ 1 ] : "Untitled" ;
console . error ( ` Failed to publish event: ${ title } ( ${ kind } ) - no relays responded ` ) ;
console . error (
throw new Error ( 'Failed to publish to any relays' ) ;
` Failed to publish event: ${ title } ( ${ kind } ) - no relays responded ` ,
) ;
throw new Error ( "Failed to publish to any relays" ) ;
}
}
} catch ( error ) {
} catch ( error ) {
const errorMessage = error instanceof Error ? error . message : 'Unknown error' ;
const errorMessage =
error instanceof Error ? error . message : "Unknown error" ;
console . error ( ` Error publishing event: ${ errorMessage } ` ) ;
console . error ( ` Error publishing event: ${ errorMessage } ` ) ;
onError ? . ( errorMessage ) ;
onError ? . ( errorMessage ) ;
return { success : false , error : errorMessage } ;
return { success : false , error : errorMessage } ;
@ -227,8 +246,8 @@ export async function publishMultipleZettels(
throw new Error ( "No valid sections found in content" ) ;
throw new Error ( "No valid sections found in content" ) ;
}
}
const allRelayUrls = Array . from ( ndk . pool ? . relays . values ( ) || [ ] ) . map ( ( r ) = >
const allRelayUrls = Array . from ( ndk . pool ? . relays . values ( ) || [ ] ) . map (
r . url
( r ) = > r . url ,
) ;
) ;
if ( allRelayUrls . length === 0 ) {
if ( allRelayUrls . length === 0 ) {
throw new Error ( "No relays available in NDK pool" ) ;
throw new Error ( "No relays available in NDK pool" ) ;
@ -266,9 +285,8 @@ export async function publishMultipleZettels(
} ) ;
} ) ;
}
}
} catch ( err ) {
} catch ( err ) {
const errorMessage = err instanceof Error
const errorMessage =
? err . message
err instanceof Error ? err . message : "Unknown error" ;
: "Unknown error" ;
results . push ( { success : false , error : errorMessage } ) ;
results . push ( { success : false , error : errorMessage } ) ;
}
}
}
}
@ -293,14 +311,87 @@ export async function publishMultipleZettels(
} ) ;
} ) ;
return results ;
return results ;
} catch ( error ) {
} catch ( error ) {
const errorMessage = error instanceof Error
const errorMessage =
? error . message
error instanceof Error ? error . message : "Unknown error" ;
: "Unknown error" ;
onError ? . ( errorMessage ) ;
onError ? . ( errorMessage ) ;
return [ { success : false , error : errorMessage } ] ;
return [ { success : false , error : errorMessage } ] ;
}
}
}
}
/ * *
* Processes publish results and extracts success / failure information
* @param results - Array of publish results
* @param events - Event objects containing content and metadata
* @param hasIndexEvent - Whether the events include an index event
* @returns Processed results with counts and event details
* /
export function processPublishResults (
results : PublishResult [ ] ,
events : { indexEvent? : any ; contentEvents : any [ ] } ,
hasIndexEvent : boolean = false ,
) : ProcessedPublishResults {
const successCount = results . filter ( ( r ) = > r . success ) . length ;
const errors = results
. filter ( ( r ) = > ! r . success && r . error )
. map ( ( r ) = > r . error ! ) ;
// Extract successful events with their titles
const successfulEvents = results
. filter ( ( r ) = > r . success && r . eventId )
. map ( ( r , index ) = > {
let title : string ;
if ( index === 0 && hasIndexEvent && events . indexEvent ) {
title = "Article Index" ;
} else {
const contentIndex = hasIndexEvent ? index - 1 : index ;
const contentEvent = events . contentEvents [ contentIndex ] ;
title =
contentEvent ? . title ||
contentEvent ? . tags ? . find ( ( t : any ) = > t [ 0 ] === "title" ) ? . [ 1 ] ||
` Note ${ contentIndex + 1 } ` ;
}
return {
eventId : r.eventId ! ,
title ,
} ;
} ) ;
// Extract failed events with their titles and errors
const failedEvents = results
. map ( ( r , index ) = > ( { result : r , index } ) )
. filter ( ( { result } ) = > ! result . success )
. map ( ( { result , index } ) = > {
let title : string ;
if ( index === 0 && hasIndexEvent && events . indexEvent ) {
title = "Article Index" ;
} else {
const contentIndex = hasIndexEvent ? index - 1 : index ;
const contentEvent = events . contentEvents [ contentIndex ] ;
title =
contentEvent ? . title ||
contentEvent ? . tags ? . find ( ( t : any ) = > t [ 0 ] === "title" ) ? . [ 1 ] ||
` Note ${ contentIndex + 1 } ` ;
}
return {
title ,
error : result.error || "Unknown error" ,
sectionIndex : index ,
} ;
} ) ;
return {
successCount ,
total : results.length ,
errors ,
successfulEvents ,
failedEvents ,
} ;
}
function generateDTag ( title : string ) : string {
function generateDTag ( title : string ) : string {
return title
return title
. toLowerCase ( )
. toLowerCase ( )