Back to Black Site Map Editor
SOURCE / PINNED RELEASE

Made of little things.

Black Site Map Editor

Release
32a57733b7e9…
Author-recorded commit
3f54890f542f…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/black-site-map-editor

Archive hash verified: 7a4b1abbac7db5b0…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/social.test.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import ts from 'typescript';
import { mapEventTemplate, parseMapEvent } from '../src/vendor/map-event.mjs';
const map = JSON.parse(await readFile(new URL('../src/vendor/outpost.json', import.meta.url)));
const author = 'a'.repeat(64),
    other = 'b'.repeat(64);
const event = {
    ...mapEventTemplate(map, { sha256: 'c'.repeat(64), size: 100 }),
    pubkey: author,
    id: 'd'.repeat(64),
};
const source = { event, metadata: parseMapEvent(event).map, relayHints: [] };
let responses = [],
    calls = [],
    published;
globalThis.__social = {
    outbox: {
        query: async (filters) => {
            calls.push(filters);
            const r = responses.shift();
            if (r instanceof Error) throw r;
            return r ?? { events: [] };
        },
        publish: async (template) => {
            published = template;
            return { ok: true, event: { ...template, pubkey: author, id: 'f'.repeat(64) } };
        },
    },
};
const code = ts
    .transpileModule(await readFile(new URL('../src/map-social.ts', import.meta.url), 'utf8'), {
        compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
    })
    .outputText.replace(
        "import { outbox } from '@napplet/sdk';",
        'const {outbox}=globalThis.__social;',
    )
    .replace(
        "from './vendor/map-event.mjs'",
        `from '${new URL('../src/vendor/map-event.mjs', import.meta.url).href}'`,
    );
const social = await import('data:text/javascript;base64,' + Buffer.from(code).toString('base64'));
const row = (event) => ({ event });
test('following uses latest own contacts, deduplicates and validates public keys', async () => {
    responses = [
        {
            events: [
                row({ kind: 3, pubkey: author, id: '1', created_at: 1, tags: [['p', author]] }),
                row({
                    kind: 3,
                    pubkey: author,
                    id: '2',
                    created_at: 2,
                    tags: [
                        ['p', other],
                        ['p', other],
                        ['p', 'bad'],
                    ],
                }),
            ],
        },
    ];
    assert.deepEqual(await social.followedAuthors(author), [other]);
    assert.deepEqual(calls.at(-1)[0].authors, [author]);
    responses = [{ events: [], incomplete: true }];
    await assert.rejects(social.followedAuthors(author), /retry/);
});
test('discussion isolates map scope, deduplicates votes and resolves only direct remixes', async () => {
    const comment = {
        kind: 1111,
        pubkey: other,
        id: '1',
        created_at: 1,
        content: 'Hello',
        tags: [
            ['A', source.metadata.address],
            ['K', '35858'],
        ],
    };
    const vote = {
        kind: 7,
        pubkey: other,
        id: '2',
        created_at: 1,
        content: '+',
        tags: [['a', source.metadata.address]],
    };
    const remix = {
        ...mapEventTemplate(
            { ...map, id: 'remix-map' },
            { sha256: 'c'.repeat(64), size: 100 },
            { remixOf: event },
        ),
        pubkey: other,
        id: '3',
    };
    responses = [
        {
            events: [
                row(comment),
                row(comment),
                row({
                    ...comment,
                    id: 'wrong',
                    tags: [
                        ['A', 'other'],
                        ['K', '35858'],
                    ],
                }),
            ],
        },
        {
            events: [
                row(vote),
                row(vote),
                row({ ...vote, id: '4', created_at: 2 }),
                row({ ...vote, id: '5', pubkey: author, content: '-' }),
            ],
        },
        { events: [row(remix), row(event)] },
    ];
    const result = await social.mapDiscussion(source);
    assert.equal(result.comments.length, 1);
    assert.deepEqual(result.reactions, [
        { label: 'Upvotes', count: 1 },
        { label: 'Downvotes', count: 1 },
    ]);
    assert.equal(result.remixes.length, 1);
    assert.equal(result.remixes[0].metadata.id, 'remix-map');
});
test('independent activity failures preserve other sections and report incomplete coverage', async () => {
    responses = [new Error('offline'), { events: [] }, { events: [], incomplete: true }];
    const result = await social.mapDiscussion(source);
    assert.match(result.notice, /could not be loaded/);
    assert.deepEqual(result.comments, []);
});
test('comment uses NIP-22 root and parent address plus event references', async () => {
    await social.postMapComment(source, ' Hello ');
    assert.equal(published.kind, 1111);
    assert.equal(published.content, 'Hello');
    for (const tag of [
        ['A', source.metadata.address],
        ['a', source.metadata.address],
        ['K', '35858'],
        ['k', '35858'],
        ['P', author],
        ['p', author],
        ['e', event.id],
    ])
        assert.ok(published.tags.some((t) => t[0] === tag[0] && t[1] === tag[1]));
    await assert.rejects(social.postMapComment(source, '  '), /1–4000/);
});