You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
Silberengel 3cb4e28934 add css 6 hours ago
integration-fixtures initial commit 6 hours ago
src add css 6 hours ago
.gitignore initial commit 6 hours ago
README.md initial commit 6 hours ago
asciidoc_testdoc.adoc initial commit 6 hours ago
package-lock.json initial commit 6 hours ago
package.json initial commit 6 hours ago
tsconfig.json initial commit 6 hours ago
vitest.config.ts initial commit 6 hours ago

README.md

gc-parser-asciidoc

Lightweight AsciiDoc parser (no asciidoctor dependency) and publication helpers for Nostr. Accepts Nostr events as JSON and returns HTML.

  • Single events (30818, 30041): pass one event JSON → get HTML.
  • Publications (30040): pass one 30040 index event + section event JSONs → get one HTML page.

Size: ~100 KB built output, zero runtime dependencies — about 80× smaller than @asciidoctor/core (~8.5 MB).

Features

  • Headings ======= with optional TOC
  • Nostr links — full bech32 (naddr1, npub1, nevent1, nprofile1, note1) and nostr:... in text become links
  • #hashtags — rendered as <span class="hashtag">#tag</span>. Style with a different color and no underline in your CSS, e.g. .hashtag { color: var(--tag-color); text-decoration: none; }
  • Wikilinks [[id]], [[id|label]]
  • URLs — bare https://... and link:url[text]; backticked URLs stay plaintext
  • Inlinebold, italic, code, strikethrough, ~sub~, ^sup^
  • Lists — nested * / **, ordered . / .., mixed

Usage

All APIs accept Nostr events as JSON: { kind, content, tags, pubkey?, id? }.

Single events (30818, 30041)

Pass one event (e.g. from a relay) and get HTML:

import { renderEvent } from 'gc-parser-asciidoc';

const event = { kind: 30041, content: '= Section\n\nBody.', tags: [['title', 'Section']], pubkey: '...' };
const html = renderEvent(event, {
  wikilinkUrl: '/events?d={dtag}',
  nostrBaseUrl: 'https://app.example.com/',
  hashtagClass: 'hashtag',
});

For raw content (no event object), use renderEventContent(content, options) or parseAsciiDoc(content, options).

Publications (kind 30040)

Pass the 30040 index event and an array of section events (30041, 30818); get one HTML page:

import { renderPublicationFromEvents } from 'gc-parser-asciidoc';

const indexEvent = { kind: 30040, content: '', tags: [['title', 'My Book'], ['d', 'my-book'], ['a', '30041:...:ch1'], ['a', '30041:...:ch2']], pubkey: '...' };
const sectionEvents = [
  { kind: 30041, content: '...', tags: [['title', 'Chapter 1'], ['d', 'ch1']], pubkey: '...' },
  { kind: 30041, content: '...', tags: [['title', 'Chapter 2'], ['d', 'ch2']], pubkey: '...' },
];

const { html, tableOfContents, asciidoc } = renderPublicationFromEvents(indexEvent, sectionEvents, { toc: true });

If you already have structured data (not raw events), use renderPublication({ index, sections }, options). Helpers: toPublicationIndexLike(event) to get PublicationIndexLike from a 30040 event; buildPublicationAsciiDoc(input) for the combined AsciiDoc string.

Integration tests

Integration tests render events from fixture files in integration-fixtures/ (no relay required). They are skipped unless RUN_INTEGRATION=1:

RUN_INTEGRATION=1 npm run test:integration

Rendered HTML is written to integration-output/single-event.html and integration-output/publication.html for inspection in a browser.

To download events and save/refresh the fixtures (fetch from wss://thecitadel.nostr1.com and overwrite integration-fixtures/*.json):

npm run test:integration:record

Commit the updated integration-fixtures/*.json so others can run the integration test without a relay. Requires nostr-tools (devDependency) only when recording.