# 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 `#tag`. 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 - **Inline** — *bold*, _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: ```ts 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: ```ts 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`: ```bash 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`): ```bash 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.