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 } from '../src/vendor/map-event.mjs';
const map = JSON.parse(
await readFile(new URL('../src/vendor/outpost.json', import.meta.url), 'utf8'),
);
const viewer = 'a'.repeat(64),
other = 'b'.repeat(64);
const source = await readFile(new URL('../src/map-host.ts', import.meta.url), 'utf8');
const js = ts
.transpileModule(source, {
compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext },
})
.outputText.replace(
/import \{ identity, outbox, resource, storage, upload \} from '@napplet\/sdk';/,
'const {identity,outbox,resource,storage,upload} = globalThis.__mapHostFixture;',
)
.replace(
"from './model'",
`from '${new URL('../src/vendor/validate-map.mjs', import.meta.url).href}'`,
)
.replace(
"from './vendor/map-event.mjs'",
`from '${new URL('../src/vendor/map-event.mjs', import.meta.url).href}'`,
);
let pubkey = viewer,
uploads = 0,
published = [],
events = [],
allow = true,
readBytes;
const drafts = new Map();
globalThis.__mapHostFixture = {
identity: { getPublicKey: async () => pubkey, onChanged: () => ({ close() {} }) },
outbox: {
query: async () => ({ events }),
publish: async (event) => {
if (!allow) return { ok: false, error: 'host policy denies kind 35858' };
const signed = { ...event, pubkey, id: 'c'.repeat(64), sig: 'd'.repeat(128) };
published.push(signed);
return { ok: true, event: signed };
},
},
resource: {
bytes: async (url) => {
if (url.startsWith('blossom:')) assert.match(url, /^blossom:sha256:[a-f0-9]{64}$/);
return new Blob([readBytes]);
},
},
storage: {
setItem: async (k, v) => drafts.set(k, v),
getItem: async (k) => drafts.get(k) ?? null,
removeItem: async (k) => drafts.delete(k),
},
upload: {
info: async () => ({ rails: [{ rail: 'blossom', enabled: true }], maxBytes: 10485760 }),
upload: async (request) => {
uploads++;
const bytes = new Uint8Array(await request.data.arrayBuffer());
assert.equal(request.noTransform, true);
assert.equal(request.rail, 'blossom');
return {
ok: true,
status: 'complete',
uploadId: 'one',
rail: 'blossom',
sha256: await host.sha256Hex(bytes),
size: bytes.length,
url: 'https://blossom.test/' + map.id,
};
},
},
};
const host = await import('data:text/javascript;base64,' + Buffer.from(js).toString('base64'));
test('publish uses native map kind and reuses verified upload after host denial', async () => {
allow = false;
events = [];
await assert.rejects(host.publishMap(map), (error) => {
assert.equal(error.upload.status, 'complete');
assert.equal(error.template.kind, 35858);
return /policy denies/.test(error.message);
});
assert.equal(uploads, 1);
assert.ok(await host.getPendingUpload(viewer, map));
allow = true;
const result = await host.publishMap(map);
assert.equal(uploads, 1);
assert.equal(result.event.kind, 35858);
assert.equal(result.metadata.id, map.id);
});
test('loads verified bytes and rejects tampered downloads', async () => {
readBytes = new TextEncoder().encode(JSON.stringify(map));
const event = {
...mapEventTemplate(map, {
sha256: await host.sha256Hex(readBytes),
size: readBytes.length,
url: 'https://blossom.test/x',
}),
pubkey: viewer,
id: 'c'.repeat(64),
sig: 'd'.repeat(128),
};
events = [];
const listRecord = {
event,
metadata: (await import('../src/vendor/map-event.mjs')).parseMapEvent(event).map,
relayHints: [],
};
assert.deepEqual(await host.loadMap(listRecord), map);
readBytes = new TextEncoder().encode(JSON.stringify({ ...map, name: 'TAMPERED' }));
await assert.rejects(host.loadMap(listRecord), /bytes|hash/);
});
test('another author cannot update or delete; drafts are partitioned by viewer', async () => {
const prior = published[0];
const parsed = (await import('../src/vendor/map-event.mjs')).parseMapEvent(prior).map;
pubkey = other;
await assert.rejects(
host.publishMap(map, { base: { event: prior, metadata: parsed, relayHints: [] } }),
/Only the author/,
);
await assert.rejects(
host.deleteMap({ event: prior, metadata: parsed, relayHints: [] }),
/identity changed/,
);
await host.saveDraft(viewer, { map });
assert.equal(await host.loadDraft(other), null);
assert.deepEqual((await host.loadDraft(viewer)).map, map);
pubkey = viewer;
});
test('stale update fails before uploading', async () => {
const prior = published[0],
parsed = (await import('../src/vendor/map-event.mjs')).parseMapEvent(prior).map;
events = [{ event: { ...prior, id: 'e'.repeat(64) } }];
const before = uploads;
await assert.rejects(
host.publishMap(map, { base: { event: prior, metadata: parsed, relayHints: [] } }),
/changed since/,
);
assert.equal(uploads, before);
});
test('malformed stored drafts and mismatched base owners are rejected', async () => {
drafts.set(`black-site-editor:draft:${viewer}`, '{"map":{}}');
await assert.rejects(host.loadDraft(viewer), /invalid map/);
const event = published[0],
metadata = (await import('../src/vendor/map-event.mjs')).parseMapEvent(event).map;
drafts.set(
`black-site-editor:draft:${other}`,
JSON.stringify({ map, base: { event, metadata, relayHints: [] } }),
);
await assert.rejects(host.loadDraft(other), /owner/);
});
test('timestamp saturated discovery reports a blocked cursor', async () => {
events = Array.from({ length: 2 }, (_, i) => ({
event: {
...published[0],
id: String(i).repeat(64),
tags: published[0].tags.map((tag) => (tag[0] === 'd' ? ['d', `map-${i}`] : tag)),
},
}));
const result = await host.discoverMaps({ limit: 2 });
assert.equal(result.paginationBlocked, true);
});
test('pending upload remains tracked when status observation fails and Retry polls it', async () => {
events = [];
allow = true;
pubkey = viewer;
const oldUpload = globalThis.__mapHostFixture.upload.upload;
const changed = { ...map, id: 'pending-map' };
const bytes = new TextEncoder().encode(JSON.stringify(changed));
let statusCalls = 0;
globalThis.__mapHostFixture.upload.upload = async () => {
uploads++;
return { ok: true, status: 'uploading', uploadId: 'pending', rail: 'blossom' };
};
globalThis.__mapHostFixture.upload.status = async () => {
statusCalls++;
if (statusCalls === 1) throw new Error('temporary observation failure');
return {
status: 'complete',
uploadId: 'pending',
rail: 'blossom',
sha256: await host.sha256Hex(bytes),
size: bytes.length,
url: 'https://blossom.test/file',
};
};
await assert.rejects(host.publishMap(changed), /observation/);
const before = uploads;
await host.publishMap(changed);
assert.equal(uploads, before);
assert.equal(statusCalls, 2);
globalThis.__mapHostFixture.upload.upload = oldUpload;
});
test('delete stops when identity switches during current revision lookup', async () => {
pubkey = viewer;
const event = published[0],
metadata = (await import('../src/vendor/map-event.mjs')).parseMapEvent(event).map;
const originalQuery = globalThis.__mapHostFixture.outbox.query;
globalThis.__mapHostFixture.outbox.query = async () => {
pubkey = other;
return { events: [{ event }] };
};
const count = published.length;
await assert.rejects(host.deleteMap({ event, metadata, relayHints: [] }), /identity changed/);
assert.equal(published.length, count);
globalThis.__mapHostFixture.outbox.query = originalQuery;
pubkey = viewer;
});
test('upload preflight rejects unsupported rail, size, MIME and synchronous missing method without uploading', async () => {
pubkey = viewer;
events = [];
const api = globalThis.__mapHostFixture.upload,
originalInfo = api.info;
const changed = { ...map, id: 'preflight-map' },
before = uploads;
for (const [info, pattern] of [
[{ rails: [{ rail: 'nip96', enabled: true }] }, /Blossom/],
[{ rails: [{ rail: 'blossom', enabled: true }], maxBytes: 1 }, /limits uploads/],
[
{ rails: [{ rail: 'blossom', enabled: true }], mimeTypes: ['image/png'] },
/application\/json/,
],
]) {
api.info = async () => info;
await assert.rejects(host.publishMap(changed), pattern);
}
api.info = () => {
throw new Error('upload.info not implemented');
};
await assert.rejects(host.publishMap(changed), /not implemented/);
assert.equal(uploads, before);
api.info = originalInfo;
});
test('new publication does not require relay reads to prove a map address is unused', async () => {
const api = globalThis.__mapHostFixture.outbox;
const original = api.query;
pubkey = viewer;
allow = true;
try {
for (const response of [
{ events: [], incomplete: true },
{ events: [], error: 'Relay unavailable' },
new Error('Read denied'),
]) {
api.query = async () => {
if (response instanceof Error) throw response;
return response;
};
const result = await host.publishMap({ ...map, id: `offline-${published.length}` });
assert.equal(result.metadata.address, `35858:${viewer}:${result.metadata.id}`);
}
} finally {
api.query = original;
}
});
test('same d-tag from another player has different coordinates and cannot block publication', async () => {
const api = globalThis.__mapHostFixture.outbox;
const original = api.query;
pubkey = viewer;
allow = true;
try {
api.query = async (filters) => {
assert.deepEqual(filters[0].authors, [viewer]);
assert.deepEqual(filters[0]['#d'], [map.id]);
return { events: [{ event: { ...published[0], pubkey: other } }] };
};
const result = await host.publishMap(map);
assert.equal(result.metadata.address, `35858:${viewer}:${map.id}`);
assert.notEqual(result.metadata.address, `35858:${other}:${map.id}`);
} finally {
api.query = original;
}
});
test('an observed own-coordinate conflict is protected even when other relay results are incomplete', async () => {
const api = globalThis.__mapHostFixture.outbox;
const original = api.query;
pubkey = viewer;
const before = uploads;
try {
api.query = async () => ({ events: [{ event: published[0] }], incomplete: true });
await assert.rejects(host.publishMap(map), /at this address under your account/);
assert.equal(uploads, before);
} finally {
api.query = original;
}
});
