Back to Atlas map viewer
SOURCE / PINNED RELEASE

Made of little things.

Atlas map viewer

Release
1d2807e80b44…
Author-recorded commit
43810ed2b368…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-4757dbabc86

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

packages/hashtree/src/announcement.ts
import { finalizeEvent, verifyEvent, type Event } from 'nostr-tools/pure';
import { parseCatalog, type MapOffer } from '../../format/src/catalog';
export const MAP_KIND = 30064;
export function relayUrl(value: string) {
  const u = new URL(value);
  if (!['ws:', 'wss:'].includes(u.protocol) || u.username || u.password || u.hash)
    throw new Error('Invalid relay URL');
  return u.href;
}
export function mapAnnouncement(
  offer: MapOffer,
  secret: Uint8Array,
  time = Math.floor(Date.now() / 1000),
): Event {
  if (!offer.hashtree) throw new Error('Index this map with Hashtree first');
  const event = finalizeEvent(
    {
      kind: MAP_KIND,
      created_at: time,
      tags: [
        ['d', `atlas/${offer.id}`],
        ['l', 'hashtree'],
        ['t', 'atlas-map'],
        ['hash', offer.hashtree.root],
        ['expiration', String(time + 7 * 86400)],
      ],
      content: JSON.stringify({
        version: 1,
        map: { ...offer, manifest: { ...offer.manifest, places: [] } },
      }),
    },
    secret,
  );
  parseAnnouncement(event, time);
  return event;
}
export function parseAnnouncement(
  event: unknown,
  now = Math.floor(Date.now() / 1000),
): { event: Event; offer: MapOffer } {
  if (JSON.stringify(event).length > 256 * 1024) throw new Error('Oversized map announcement');
  // Caller-owned nostr-tools objects may carry a cached verification symbol.
  const e = JSON.parse(JSON.stringify(event)) as Event;
  if (
    !e ||
    e.kind !== MAP_KIND ||
    !verifyEvent(e) ||
    e.created_at > now + 300 ||
    !e.tags.some((t) => t[0] === 'l' && t[1] === 'hashtree') ||
    !e.tags.some((t) => t[0] === 't' && t[1] === 'atlas-map')
  )
    throw new Error('Invalid signed map announcement');
  const tag = (key: string) => {
    const ts = e.tags.filter((t) => t[0] === key);
    if (ts.length !== 1) throw new Error(`Invalid ${key} tag`);
    return ts[0]![1]!;
  };
  const expiration = Number(tag('expiration'));
  if (
    !Number.isSafeInteger(expiration) ||
    expiration <= now ||
    expiration > e.created_at + 7 * 86400
  )
    throw new Error('Expired map announcement');
  if (e.tags.some((t) => ['key', 'encryptedKey', 'selfEncryptedKey'].includes(t[0]!)))
    throw new Error('Encrypted map offers are not supported yet');
  const content = JSON.parse(e.content);
  if (content.version !== 1) throw new Error('Unsupported map announcement');
  const offer = parseCatalog({ maps: [content.map] }, content.map.url)[0]!;
  if (!offer.hashtree || offer.hashtree.root !== tag('hash') || tag('d') !== `atlas/${offer.id}`)
    throw new Error('Map root or identity mismatch');
  return { event: e, offer };
}