Back to Napcraft
SOURCE / PINNED RELEASE

Made of little things.

Napcraft

Release
ee47ac6a25df…
Author-recorded commit
410dea87e109…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce

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

src/world-directory.ts
import { identity, outbox } from '@napplet/sdk';
import { appDataCollection, type AppDataHost, type AppRecord } from '../docs/examples/app-data';

export type WorldListing = { code: string };
export type ListedWorld = AppRecord<WorldListing>;

/** Advertisements may point only to ordinary Napcraft codes, never external URLs. */
export function validateListing(value: unknown): WorldListing {
  if (!value || typeof value !== 'object' || Array.isArray(value) ||
      Object.keys(value).join(',') !== 'code') throw new Error('Invalid world listing.');
  const code = (value as WorldListing).code;
  if (typeof code !== 'string' || code.length > 300 || !/^NC1-[A-Za-z0-9_-]+$/.test(code))
    throw new Error('Invalid world code in listing.');
  let parts: unknown;
  try { parts = JSON.parse(atob(code.slice(4).replaceAll('-', '+').replaceAll('_', '/'))); }
  catch { throw new Error('Invalid world code in listing.'); }
  if (!Array.isArray(parts) || parts.length !== 2 || typeof parts[0] !== 'string' ||
      !/^[a-f0-9]{64}$/.test(parts[0]) || typeof parts[1] !== 'string' ||
      !/^[A-Za-z0-9_-]{1,64}$/.test(parts[1])) throw new Error('Invalid world code in listing.');
  return { code: 'NC1-' + btoa(JSON.stringify(parts)).replaceAll('+','-').replaceAll('/','_').replace(/=+$/,'') };
}

export async function listingId(code: string) {
  const bytes = new TextEncoder().encode(validateListing({code}).code);
  const digest = await crypto.subtle.digest('SHA-256', bytes);
  return [...new Uint8Array(digest)].map(b=>b.toString(16).padStart(2,'0')).join('');
}

export function distinctWorlds(records: ListedWorld[], viewer = '') {
  const byCode = new Map<string, ListedWorld>();
  for (const record of records) {
    if (!record.data || record.deleted) continue;
    const old = byCode.get(record.data.code);
    if (!old || (record.author===viewer && old.author!==viewer) ||
        (old.author!==viewer && (record.createdAt>old.createdAt ||
        (record.createdAt===old.createdAt && record.revision<old.revision)))) byCode.set(record.data.code,record);
  }
  return [...byCode.values()].sort((a,b)=>b.createdAt-a.createdAt || a.revision.localeCompare(b.revision));
}

export async function openWorldDirectory() {
  // The shipped soyLI app-data helper observes this host-policy hint. SDK 0.24.4
  // has no shell export; this is not an app-owned handshake or network path.
  const injected = (globalThis as unknown as {napplet?:AppDataHost}).napplet;
  if (!injected?.shell?.onReady) throw new Error('This host does not offer the public world list. You can still join with a code.');
  return appDataCollection<WorldListing>({
    collection:'worlds', schema:'napcraft.world-directory', version:1,
    validate:validateListing, host:{shell:injected.shell,identity,outbox:{
      query:(filters,options)=>outbox.query(filters as Parameters<typeof outbox.query>[0],options),
      publish:(template,options)=>outbox.publish(template,options),
    }},
  });
}

export type WorldDirectory = Awaited<ReturnType<typeof openWorldDirectory>>;
export type ListingChange = Awaited<ReturnType<WorldDirectory['prepare']>>;