atmo.rsvp
1import type { ContrailConfig } from '@atmo-dev/contrail';
2import { SPACE_TYPE } from './spaces/config';
3
4export const config: ContrailConfig = {
5 namespace: 'rsvp.atmo',
6 // Enable the rsvp.atmo.notifyOfUpdate endpoint. The client calls it after
7 // writing records to the PDS so contrail re-fetches and indexes them
8 // immediately instead of waiting for the jetstream.
9 notify: true,
10 // `spaces` is declared statically so `pnpm generate` emits the `rsvp.atmo.space.*`
11 // lexicons. The real serviceDid is injected at runtime in `$lib/contrail/index.ts`
12 // via `getSpacesConfig()` — generate doesn't serialize it.
13 spaces: { type: SPACE_TYPE, serviceDid: 'did:web:placeholder' },
14 permissionSet: {
15 title: 'Atmo Events',
16 description: 'Manage your private events and rsvps.'
17 // NOTE: permission-set lexicons can only reference NSIDs under their own
18 // namespace (`rsvp.atmo.*`). Repo writes for `community.lexicon.*` and
19 // blob uploads are declared as standalone `scope.repo(...)` /
20 // `scope.blob(...)` entries in `atproto/settings.ts`, not here.
21 },
22 collections: {
23 event: {
24 collection: 'community.lexicon.calendar.event',
25 queryable: {
26 mode: {},
27 name: {},
28 status: {},
29 description: {},
30 'preferences.showInDiscovery': {},
31 startsAt: { type: 'range' },
32 endsAt: { type: 'range' },
33 createdAt: { type: 'range' }
34 },
35 searchable: ['mode', 'name', 'status', 'description'],
36 relations: {
37 rsvps: {
38 collection: 'rsvp',
39 groupBy: 'status',
40 groups: {
41 going: 'community.lexicon.calendar.rsvp#going',
42 interested: 'community.lexicon.calendar.rsvp#interested',
43 notgoing: 'community.lexicon.calendar.rsvp#notgoing'
44 }
45 }
46 },
47 pipelineQueries: {
48 // Endpoint: rsvp.atmo.event.listDiscoverable
49 // Same shape as listRecords, but filters out unlisted events
50 // (preferences.showInDiscovery === false). Missing field defaults
51 // to true, so pre-existing records without `preferences` are included.
52 listDiscoverable: async () => ({
53 conditions: [
54 `(json_extract(r.record, '$.preferences.showInDiscovery') IS NULL
55 OR json_extract(r.record, '$.preferences.showInDiscovery') != 0)`
56 ]
57 })
58 }
59 },
60 rsvp: {
61 collection: 'community.lexicon.calendar.rsvp',
62 queryable: {
63 status: {},
64 'subject.uri': {},
65 createdAt: { type: 'range' }
66 },
67 references: {
68 event: {
69 collection: 'event',
70 field: 'subject.uri'
71 }
72 }
73 }
74 // `follow` (app.bsky.graph.follow) is auto-added by contrail 0.5+ when
75 // `feeds` is configured: discover:false, subjectField:'subject' (so only
76 // follows whose subject is already in identities are indexed).
77 },
78 feeds: {
79 // Exposed as rsvp.atmo.getFeed?feed=network&actor=<did>&collection=<nsid>.
80 // Powers the home-page "from people you follow" surface.
81 network: {
82 // Per-target caps so RSVPs (high-volume) can't squeeze events
83 // (low-volume) out of the cap. Bumped above the default 200 because
84 // most RSVPs in feed_items refer to past events, and we want enough
85 // breathing room to find recent ones after the JS-side filter.
86 targets: [
87 { collection: 'event', maxItems: 200 },
88 { collection: 'rsvp', maxItems: 1000 }
89 ]
90 }
91 }
92};