publish(task 6): kind:30023 event-builder mit tag-mapping
buildKind30023(args) baut unsigniertes kind:30023-event aus frontmatter + rewritten-body + cover-url. erzeugt pflicht-tags (d, title, published_at) und bedingt optionale (summary aus description, image aus coverUrl, t-tags aus tags[], client aus clientTag). plus additionalTags-parameter für spätere task 15: license-tag und imeta-tags (mit blossom-sha256) werden dort nach dem upload angehängt. 4 tests grün. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a6c5bd26e7
commit
e4518fbf69
|
|
@ -0,0 +1,43 @@
|
|||
import type { Frontmatter } from './frontmatter.ts'
|
||||
|
||||
export interface UnsignedEvent {
|
||||
kind: number
|
||||
pubkey: string
|
||||
created_at: number
|
||||
tags: string[][]
|
||||
content: string
|
||||
}
|
||||
|
||||
export interface BuildArgs {
|
||||
fm: Frontmatter
|
||||
rewrittenBody: string
|
||||
coverUrl: string | undefined
|
||||
pubkeyHex: string
|
||||
clientTag: string
|
||||
nowSeconds: number
|
||||
additionalTags?: string[][]
|
||||
}
|
||||
|
||||
export function buildKind30023(args: BuildArgs): UnsignedEvent {
|
||||
const { fm, rewrittenBody, coverUrl, pubkeyHex, clientTag, nowSeconds, additionalTags } = args
|
||||
const publishedAt = Math.floor(fm.date.getTime() / 1000)
|
||||
const tags: string[][] = [
|
||||
['d', fm.slug],
|
||||
['title', fm.title],
|
||||
['published_at', String(publishedAt)],
|
||||
]
|
||||
if (fm.description) tags.push(['summary', fm.description])
|
||||
if (coverUrl) tags.push(['image', coverUrl])
|
||||
if (Array.isArray(fm.tags)) {
|
||||
for (const t of fm.tags) tags.push(['t', String(t)])
|
||||
}
|
||||
if (clientTag) tags.push(['client', clientTag])
|
||||
if (additionalTags) tags.push(...additionalTags)
|
||||
return {
|
||||
kind: 30023,
|
||||
pubkey: pubkeyHex,
|
||||
created_at: nowSeconds,
|
||||
tags,
|
||||
content: rewrittenBody,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import { assertEquals } from '@std/assert'
|
||||
import { buildKind30023 } from '../src/core/event.ts'
|
||||
import type { Frontmatter } from '../src/core/frontmatter.ts'
|
||||
|
||||
const PUBKEY = '4fa5d1c413e2b45e10d40bf3562ab701a5331206e359c90baae0e99bfd6c6e41'
|
||||
|
||||
Deno.test('buildKind30023: minimaler Post liefert alle Pflicht-Tags', () => {
|
||||
const fm: Frontmatter = {
|
||||
title: 'Hello',
|
||||
slug: 'hello',
|
||||
date: new Date('2024-01-15T00:00:00Z'),
|
||||
}
|
||||
const ev = buildKind30023({
|
||||
fm,
|
||||
rewrittenBody: 'body text',
|
||||
coverUrl: undefined,
|
||||
pubkeyHex: PUBKEY,
|
||||
clientTag: 'test-client',
|
||||
nowSeconds: 1_700_000_000,
|
||||
})
|
||||
assertEquals(ev.kind, 30023)
|
||||
assertEquals(ev.pubkey, PUBKEY)
|
||||
assertEquals(ev.created_at, 1_700_000_000)
|
||||
assertEquals(ev.content, 'body text')
|
||||
const tags = ev.tags
|
||||
assertEquals(tags.find((t) => t[0] === 'd'), ['d', 'hello'])
|
||||
assertEquals(tags.find((t) => t[0] === 'title'), ['title', 'Hello'])
|
||||
assertEquals(
|
||||
tags.find((t) => t[0] === 'published_at')?.[1],
|
||||
String(Math.floor(Date.UTC(2024, 0, 15) / 1000)),
|
||||
)
|
||||
assertEquals(tags.find((t) => t[0] === 'client'), ['client', 'test-client'])
|
||||
})
|
||||
|
||||
Deno.test('buildKind30023: mapping summary / image / tags', () => {
|
||||
const fm: Frontmatter = {
|
||||
title: 'T',
|
||||
slug: 's',
|
||||
date: new Date('2024-01-01'),
|
||||
description: 'Summary text',
|
||||
tags: ['Foo', 'Bar Baz'],
|
||||
}
|
||||
const ev = buildKind30023({
|
||||
fm,
|
||||
rewrittenBody: 'b',
|
||||
coverUrl: 'https://bl.example/cover-hash.png',
|
||||
pubkeyHex: PUBKEY,
|
||||
clientTag: 'x',
|
||||
nowSeconds: 1,
|
||||
})
|
||||
assertEquals(ev.tags.find((t) => t[0] === 'summary'), ['summary', 'Summary text'])
|
||||
assertEquals(
|
||||
ev.tags.find((t) => t[0] === 'image'),
|
||||
['image', 'https://bl.example/cover-hash.png'],
|
||||
)
|
||||
assertEquals(
|
||||
ev.tags.filter((t) => t[0] === 't'),
|
||||
[['t', 'Foo'], ['t', 'Bar Baz']],
|
||||
)
|
||||
})
|
||||
|
||||
Deno.test('buildKind30023: ohne coverUrl kein image-tag', () => {
|
||||
const fm: Frontmatter = {
|
||||
title: 'T',
|
||||
slug: 's',
|
||||
date: new Date('2024-01-01'),
|
||||
}
|
||||
const ev = buildKind30023({
|
||||
fm,
|
||||
rewrittenBody: 'b',
|
||||
coverUrl: undefined,
|
||||
pubkeyHex: PUBKEY,
|
||||
clientTag: 'x',
|
||||
nowSeconds: 1,
|
||||
})
|
||||
assertEquals(ev.tags.some((t) => t[0] === 'image'), false)
|
||||
})
|
||||
|
||||
Deno.test('buildKind30023: leerer clientTag wird weggelassen', () => {
|
||||
const fm: Frontmatter = {
|
||||
title: 'T',
|
||||
slug: 's',
|
||||
date: new Date('2024-01-01'),
|
||||
}
|
||||
const ev = buildKind30023({
|
||||
fm,
|
||||
rewrittenBody: 'b',
|
||||
coverUrl: undefined,
|
||||
pubkeyHex: PUBKEY,
|
||||
clientTag: '',
|
||||
nowSeconds: 1,
|
||||
})
|
||||
assertEquals(ev.tags.some((t) => t[0] === 'client'), false)
|
||||
})
|
||||
Loading…
Reference in New Issue