Browse Source

Generate Nostr events from parsed AsciiDoc block tree

master
buttercat1791 2 years ago
parent
commit
0ec9e624e6
  1. 178
      src/lib/parser.ts

178
src/lib/parser.ts

@ -1,8 +1,28 @@ @@ -1,8 +1,28 @@
import { AbstractNode, Asciidoctor, Block, Document, Extensions, Section, type ProcessorOptions } from 'asciidoctor';
import NDK, { NDKEvent } from '@nostr-dev-kit/ndk';
import {
AbstractBlock,
AbstractNode,
Asciidoctor,
Block,
Document,
Extensions,
Section,
type ProcessorOptions
} from 'asciidoctor';
export default class Pharos extends Asciidoctor {
private ndk: NDK;
/**
* The HTML content of the converted document.
*/
private html?: string | Document;
/**
* The ID of the root node in the document.
*/
private rootId?: string;
/**
* A map of node IDs to the nodes themselves.
*/
@ -18,11 +38,18 @@ export default class Pharos extends Asciidoctor { @@ -18,11 +38,18 @@ export default class Pharos extends Asciidoctor {
*/
private indices: Map<string, Set<string>> = new Map<string, Set<string>>();
constructor() {
/**
* A map of node IDs to the Nostr event IDs of the events they generate.
*/
private eventIds: Map<string, string> = new Map<string, string>();
constructor(ndk: NDK) {
super();
const pharos = this;
pharos.Extensions.register(function () {
this.ndk = ndk;
const pharos = this;
this.Extensions.register(function () {
const registry = this;
registry.treeProcessor(function () {
const dsl = this;
@ -36,9 +63,19 @@ export default class Pharos extends Asciidoctor { @@ -36,9 +63,19 @@ export default class Pharos extends Asciidoctor {
parse(content: string, options?: ProcessorOptions | undefined): void {
this.html = this.convert(content, options) as string | Document | undefined;
this.Block
}
getEvents(pubkey: string): NDKEvent[] {
const stack = this.stackEventNodes(this.rootId!);
return this.generateEvents(stack, pubkey);
}
getHtml(): string {
return this.html?.toString() || '';
}
// #region Tree Processor Extensions
/**
* Walks the Asciidoctor Abstract Syntax Tree (AST) and performs the following mappings:
* - Each node ID is mapped to the node itself.
@ -46,10 +83,10 @@ export default class Pharos extends Asciidoctor { @@ -46,10 +83,10 @@ export default class Pharos extends Asciidoctor {
* - Each ID of a node containing children is mapped to the set of IDs of its children.
*/
private treeProcessor(treeProcessor: Extensions.TreeProcessor, document: Document) {
const id = document.getId();
this.nodes.set(id, document);
this.kinds.set(id, 30040);
this.indices.set(id, new Set<string>());
this.rootId = document.getId();
this.nodes.set(this.rootId, document);
this.kinds.set(this.rootId, 30040);
this.indices.set(this.rootId, new Set<string>());
/** FIFO queue (uses `Array.push()` and `Array.shift()`). */
const queue: AbstractNode[] = document.getBlocks();
@ -128,4 +165,127 @@ export default class Pharos extends Asciidoctor { @@ -128,4 +165,127 @@ export default class Pharos extends Asciidoctor {
// Add the block to its parent index.
this.indices.get(parentId)?.add(id);
}
//#endregion
// #region NDKEvent Generation
/**
* Generates a stack of node IDs such that processing them in LIFO order will generate any events
* used by an index before generating that index itself.
* @param rootNodeId The ID of the node from which to start processing.
* @returns An array of node IDs in the order they should be processed to generate events.
*/
private stackEventNodes(rootNodeId: string): string[] {
const traversalStack: string[] = [this.rootId!];
const eventStack: string[] = [];
while (traversalStack.length > 0) {
const parentId = traversalStack.pop()!;
eventStack.push(parentId);
if (!this.indices.has(parentId)) {
continue;
}
const childIds = Array.from(this.indices.get(parentId)!);
traversalStack.push(...childIds);
}
return eventStack;
}
/**
* Generates Nostr events for each node in the given stack.
* @param nodeIdStack An array of node IDs ordered such that processing them in LIFO order will
* produce any child event before it is required by a parent index event.
* @param pubkey The public key (not encoded in npub form) of the user generating the events.
* @returns An array of Nostr events.
*/
private generateEvents(nodeIdStack: string[], pubkey: string): NDKEvent[] {
const events: NDKEvent[] = [];
while (nodeIdStack.length > 0) {
const nodeId = nodeIdStack.pop();
let event: NDKEvent;
switch (this.kinds.get(nodeId!)) {
case 30040:
events.push(this.generateIndexEvent(nodeId!, pubkey));
break;
case 30041:
default:
// Kind 30041 is currently the default contentful kind.
events.push(this.generateZettelEvent(nodeId!, pubkey));
break;
}
}
return events;
}
/**
* Generates a kind 30040 index event for the node with the given ID.
* @param nodeId The ID of the AsciiDoc document node from which to generate an index event. The
* node ID will be used as the event's unique d tag identifier.
* @param pubkey The public key (not encoded in npub form) of the user generating the events.
* @returns An unsigned NDKEvent with the requisite tags, including e tags pointing to each of its
* children, and dated to the present moment.
*/
private generateIndexEvent(nodeId: string, pubkey: string): NDKEvent {
const title = (this.nodes.get(nodeId)! as AbstractBlock).getTitle();
const childTags = Array.from(this.indices.get(nodeId)!)
.map(id => ['#e', this.eventIds.get(id)!]);
const event = new NDKEvent(this.ndk);
event.kind = 30040;
event.content = '';
event.tags = [
['title', title!],
['#d', nodeId],
...childTags
];
event.created_at = Date.now();
event.pubkey = pubkey;
// Event ID generation must be the last step.
const eventId = event.getEventHash();
this.eventIds.set(nodeId, eventId);
event.id = eventId;
return event;
}
/**
* Generates a kind 30041 zettel event for the node with the given ID.
* @param nodeId The ID of the AsciiDoc document node from which to generate an index event. The
* node ID will be used as the event's unique d tag identifier.
* @param pubkey The public key (not encoded in npub form) of the user generating the events.
* @returns An unsigned NDKEvent containing the content of the zettel, the requisite tags, and
* dated to the present moment.
*/
private generateZettelEvent(nodeId: string, pubkey: string): NDKEvent {
const title = (this.nodes.get(nodeId)! as Block).getTitle();
const content = (this.nodes.get(nodeId)! as Block).getSource(); // AsciiDoc source content.
const event = new NDKEvent(this.ndk);
event.kind = 30041;
event.content = content!;
event.tags = [
['title', title!],
['#d', nodeId]
];
event.created_at = Date.now();
event.pubkey = pubkey;
// Event ID generation must be the last step.
const eventId = event.getEventHash();
this.eventIds.set(nodeId, eventId);
event.id = eventId;
return event;
}
// #endregion
}
Loading…
Cancel
Save