~james/core

core/sitemap/src/hydrant.ts -rw-r--r-- 3.2 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 16 hours ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import type { JetstreamEvent } from '@atcute/jetstream';
import type { RecordValue, UrlEntry } from './types';
import { sanitizeLastmod } from './xml';

// deletes carry no record and only the at-uri finds the url
export type ExtractedAction =
  | { type: 'delete'; uri: string }
  | { type: 'upsert'; uri: string; did: string; url: string; entry: UrlEntry; claim?: string };

interface Located {
  owner: string;
  path: string;
  // the url only shows once a declaration at this uri points at the record's did
  claim?: string;
}

interface CollectionSpec {
  locate: (did: string, rkey: string, record: RecordValue) => Located | null;
  changefreq: UrlEntry['changefreq'];
  priority: number;
}

export const DECLARATION = 'org.tangled.repo.declaration';

export const COLLECTION_SPECS: Record<string, CollectionSpec> = {
  // owner comes from the record, not the did the record lives in
  'org.tangled.repo.manifest': {
    locate: (_did, _rkey, record) => {
      const { owner, name } = (record.declaration ?? {}) as { owner?: unknown; name?: unknown };
      return typeof owner === 'string' && typeof name === 'string' && owner.startsWith('did:') && name
        ? { owner, path: `${owner}/${name}`, claim: `at://${owner}/${DECLARATION}/${name.toLowerCase()}` }
        : null;
    },
    changefreq: 'daily',
    priority: 0.8,
  },
  'org.tangled.actor.profile': {
    locate: (did) => ({ owner: did, path: did }),
    changefreq: 'daily',
    priority: 0.8,
  },
  'org.tangled.snippet.string': {
    locate: (did, rkey) => ({ owner: did, path: `strings/${did}/${rkey}` }),
    changefreq: 'weekly',
    priority: 0.6,
  },
};

// owners are dids here so a url never moves when someone changes handle, and
// rendering swaps the handle in
export function extractUrlAction(
  event: JetstreamEvent,
  baseUrl: string,
  now = new Date(),
): ExtractedAction | null {
  if (event.kind !== 'commit') {
    return null;
  }

  const { collection, rkey } = event.commit;
  const spec = COLLECTION_SPECS[collection];
  if (!spec) {
    return null;
  }

  const uri = `at://${event.did}/${collection}/${rkey}`;
  if (event.commit.operation === 'delete') {
    return { type: 'delete', uri };
  }

  const record = event.commit.record as RecordValue;
  const located = spec.locate(event.did, rkey, record);
  if (!located) {
    return { type: 'delete', uri };
  }

  const url = `${baseUrl.replace(/\/+$/, '')}/${located.path}`;
  const lastmod = sanitizeLastmod(record.createdAt as string | undefined, now);
  return {
    type: 'upsert',
    uri,
    did: located.owner,
    url,
    ...(located.claim && { claim: located.claim }),
    entry: {
      loc: url,
      lastmod,
      changefreq: spec.changefreq,
      priority: spec.priority,
    },
  };
}

export function extractDeclaration(event: JetstreamEvent): { uri: string; repo: string | null } | null {
  if (event.kind !== 'commit' || event.commit.collection !== DECLARATION) {
    return null;
  }
  const uri = `at://${event.did}/${DECLARATION}/${event.commit.rkey}`;
  if (event.commit.operation === 'delete') {
    return { uri, repo: null };
  }
  const { repo } = event.commit.record as RecordValue;
  return { uri, repo: typeof repo === 'string' ? repo : null };
}