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.
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/);
});
