diff --git a/apps/atmo-rsvp/lex.config.js b/apps/atmo-rsvp/lex.config.js index e710bda..2447175 100644 --- a/apps/atmo-rsvp/lex.config.js +++ b/apps/atmo-rsvp/lex.config.js @@ -18,6 +18,9 @@ export default defineLexiconConfig({ type: "atproto", mode: "nsids", nsids: [ + "app.bsky.actor.profile", + "app.bsky.graph.follow", + "com.atproto.label.defs", "com.atproto.repo.strongRef", "community.lexicon.calendar.event", "community.lexicon.calendar.rsvp", diff --git a/apps/atmo-rsvp/lexicons/generated/index.ts b/apps/atmo-rsvp/lexicons/generated/index.ts index 7d5efaa..f1d79b9 100644 --- a/apps/atmo-rsvp/lexicons/generated/index.ts +++ b/apps/atmo-rsvp/lexicons/generated/index.ts @@ -1,17 +1,23 @@ // Auto-generated by @atmo-dev/contrail. Do not edit. // Regenerate with `contrail lexicons generate`. -import _0 from "../pulled/com/atproto/repo/strongRef.json"; -import _1 from "../pulled/community/lexicon/calendar/event.json"; -import _2 from "../pulled/community/lexicon/calendar/rsvp.json"; -import _3 from "../pulled/community/lexicon/location/address.json"; -import _4 from "../pulled/community/lexicon/location/fsq.json"; -import _5 from "../pulled/community/lexicon/location/geo.json"; -import _6 from "../pulled/community/lexicon/location/hthree.json"; -import _7 from "./rsvp/atmo/event/getRecord.json"; -import _8 from "./rsvp/atmo/event/listRecords.json"; -import _9 from "./rsvp/atmo/getCursor.json"; -import _10 from "./rsvp/atmo/rsvp/getRecord.json"; -import _11 from "./rsvp/atmo/rsvp/listRecords.json"; +import _0 from "../pulled/app/bsky/actor/profile.json"; +import _1 from "../pulled/app/bsky/graph/follow.json"; +import _2 from "../pulled/com/atproto/label/defs.json"; +import _3 from "../pulled/com/atproto/repo/strongRef.json"; +import _4 from "../pulled/community/lexicon/calendar/event.json"; +import _5 from "../pulled/community/lexicon/calendar/rsvp.json"; +import _6 from "../pulled/community/lexicon/location/address.json"; +import _7 from "../pulled/community/lexicon/location/fsq.json"; +import _8 from "../pulled/community/lexicon/location/geo.json"; +import _9 from "../pulled/community/lexicon/location/hthree.json"; +import _10 from "./rsvp/atmo/event/getRecord.json"; +import _11 from "./rsvp/atmo/event/listRecords.json"; +import _12 from "./rsvp/atmo/getCursor.json"; +import _13 from "./rsvp/atmo/getFeed.json"; +import _14 from "./rsvp/atmo/getProfile.json"; +import _15 from "./rsvp/atmo/notifyOfUpdate.json"; +import _16 from "./rsvp/atmo/rsvp/getRecord.json"; +import _17 from "./rsvp/atmo/rsvp/listRecords.json"; -export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11]; +export const lexicons: object[] = [_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17]; diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/getRecord.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/getRecord.json index 31fa80b..0b9e019 100644 --- a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/getRecord.json +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/getRecord.json @@ -16,6 +16,10 @@ "format": "at-uri", "description": "AT URI of the record" }, + "profiles": { + "type": "boolean", + "description": "Include indexed profile and identity information" + }, "hydrateRsvps": { "type": "integer", "minimum": 1, @@ -82,6 +86,13 @@ "rsvps": { "type": "ref", "ref": "#hydrateRsvps" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } } } } @@ -158,6 +169,40 @@ } } } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } } } } diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/listRecords.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/listRecords.json index 1e49974..43e8c64 100644 --- a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/listRecords.json +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/event/listRecords.json @@ -22,6 +22,10 @@ "format": "at-identifier", "description": "Filter by an indexed DID or cached handle" }, + "profiles": { + "type": "boolean", + "description": "Include indexed profile and identity information" + }, "search": { "type": "string", "description": "Full-text search across: name, description" @@ -122,6 +126,13 @@ }, "cursor": { "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } } } } @@ -258,6 +269,40 @@ } } } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } } } } diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getFeed.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getFeed.json new file mode 100644 index 0000000..e971631 --- /dev/null +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getFeed.json @@ -0,0 +1,419 @@ +{ + "lexicon": 1, + "id": "rsvp.atmo.getFeed", + "defs": { + "main": { + "type": "query", + "description": "Get a configured personalized feed", + "parameters": { + "type": "params", + "required": [ + "feed", + "actor" + ], + "properties": { + "feed": { + "type": "string", + "knownValues": [ + "network" + ] + }, + "actor": { + "type": "string", + "format": "at-identifier", + "description": "DID or handle whose feed should be queried" + }, + "collection": { + "type": "string", + "knownValues": [ + "community.lexicon.calendar.event", + "community.lexicon.calendar.rsvp" + ] + }, + "limit": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "default": 50 + }, + "cursor": { + "type": "string" + }, + "profiles": { + "type": "boolean" + }, + "search": { + "type": "string", + "description": "Full-text search across: name, description" + }, + "createdAtMin": { + "type": "string", + "description": "Minimum value for createdAt" + }, + "createdAtMax": { + "type": "string", + "description": "Maximum value for createdAt" + }, + "endsAtMin": { + "type": "string", + "description": "Minimum value for endsAt" + }, + "endsAtMax": { + "type": "string", + "description": "Maximum value for endsAt" + }, + "mode": { + "type": "string", + "description": "Filter by mode" + }, + "startsAtMin": { + "type": "string", + "description": "Minimum value for startsAt" + }, + "startsAtMax": { + "type": "string", + "description": "Maximum value for startsAt" + }, + "status": { + "type": "string", + "description": "Filter by status" + }, + "rsvpsCountMin": { + "type": "integer", + "description": "Minimum total rsvps count" + }, + "rsvpsGoingCountMin": { + "type": "integer", + "description": "Minimum rsvps count where status = going" + }, + "rsvpsInterestedCountMin": { + "type": "integer", + "description": "Minimum rsvps count where status = interested" + }, + "rsvpsNotgoingCountMin": { + "type": "integer", + "description": "Minimum rsvps count where status = notgoing" + }, + "hydrateRsvps": { + "type": "integer", + "minimum": 1, + "maximum": 50, + "description": "Number of rsvps records to embed" + }, + "sort": { + "type": "string", + "knownValues": [ + "createdAt", + "endsAt", + "mode", + "rsvpsCount", + "rsvpsGoingCount", + "rsvpsInterestedCount", + "rsvpsNotgoingCount", + "startsAt", + "status", + "subjectUri" + ], + "description": "Field to sort by (default: time_us)" + }, + "order": { + "type": "string", + "knownValues": [ + "asc", + "desc" + ], + "description": "Sort direction" + }, + "subjectUri": { + "type": "string", + "description": "Filter by subject.uri" + }, + "hydrateEvent": { + "type": "boolean", + "description": "Embed the referenced event record" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": [ + "records" + ], + "properties": { + "records": { + "type": "array", + "items": { + "type": "union", + "refs": [ + "#feedRecordEvent", + "#feedRecordRsvp" + ] + } + }, + "cursor": { + "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } + } + } + } + } + }, + "feedRecordEvent": { + "type": "object", + "required": [ + "uri", + "cid", + "value", + "did", + "collection", + "rkey", + "time_us" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "community.lexicon.calendar.event#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + }, + "rsvpsCount": { + "type": "integer", + "description": "Total rsvps count" + }, + "rsvpsGoingCount": { + "type": "integer", + "description": "rsvps count where status = going" + }, + "rsvpsInterestedCount": { + "type": "integer", + "description": "rsvps count where status = interested" + }, + "rsvpsNotgoingCount": { + "type": "integer", + "description": "rsvps count where status = notgoing" + }, + "rsvps": { + "type": "ref", + "ref": "#hydrateRsvps" + } + } + }, + "feedRecordRsvp": { + "type": "object", + "required": [ + "uri", + "cid", + "value", + "did", + "collection", + "rkey", + "time_us" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "community.lexicon.calendar.rsvp#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + }, + "event": { + "type": "ref", + "ref": "#refEventRecord" + } + } + }, + "hydrateRsvpsRecord": { + "type": "object", + "required": [ + "uri", + "value", + "did", + "collection", + "rkey", + "time_us" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "community.lexicon.calendar.rsvp#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + } + } + }, + "hydrateRsvps": { + "type": "object", + "properties": { + "going": { + "type": "array", + "items": { + "type": "ref", + "ref": "#hydrateRsvpsRecord" + } + }, + "interested": { + "type": "array", + "items": { + "type": "ref", + "ref": "#hydrateRsvpsRecord" + } + }, + "notgoing": { + "type": "array", + "items": { + "type": "ref", + "ref": "#hydrateRsvpsRecord" + } + }, + "other": { + "type": "array", + "items": { + "type": "ref", + "ref": "#hydrateRsvpsRecord" + } + } + } + }, + "refEventRecord": { + "type": "object", + "required": [ + "uri", + "value", + "did", + "collection", + "rkey", + "time_us" + ], + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "community.lexicon.calendar.event#main" + }, + "did": { + "type": "string", + "format": "did" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + }, + "time_us": { + "type": "integer" + } + } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } + } + } +} diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getProfile.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getProfile.json new file mode 100644 index 0000000..54e562c --- /dev/null +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/getProfile.json @@ -0,0 +1,73 @@ +{ + "lexicon": 1, + "id": "rsvp.atmo.getProfile", + "defs": { + "main": { + "type": "query", + "parameters": { + "type": "params", + "required": [ + "actor" + ], + "properties": { + "actor": { + "type": "string", + "format": "at-identifier" + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": [ + "profiles" + ], + "properties": { + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } + } + } + } + } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } + } + } +} diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/notifyOfUpdate.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/notifyOfUpdate.json new file mode 100644 index 0000000..5d7e545 --- /dev/null +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/notifyOfUpdate.json @@ -0,0 +1,56 @@ +{ + "lexicon": 1, + "id": "rsvp.atmo.notifyOfUpdate", + "defs": { + "main": { + "type": "procedure", + "description": "Fetch changed records from their authoritative PDS for immediate indexing", + "input": { + "encoding": "application/json", + "schema": { + "type": "object", + "properties": { + "uri": { + "type": "string", + "format": "at-uri" + }, + "uris": { + "type": "array", + "items": { + "type": "string", + "format": "at-uri" + }, + "maxLength": 25 + } + } + } + }, + "output": { + "encoding": "application/json", + "schema": { + "type": "object", + "required": [ + "indexed", + "deleted" + ], + "properties": { + "indexed": { + "type": "integer", + "minimum": 0 + }, + "deleted": { + "type": "integer", + "minimum": 0 + }, + "errors": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } +} diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/getRecord.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/getRecord.json index 1a9ab26..99016e4 100644 --- a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/getRecord.json +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/getRecord.json @@ -16,6 +16,10 @@ "format": "at-uri", "description": "AT URI of the record" }, + "profiles": { + "type": "boolean", + "description": "Include indexed profile and identity information" + }, "hydrateEvent": { "type": "boolean", "description": "Embed the referenced event record" @@ -64,6 +68,13 @@ "event": { "type": "ref", "ref": "#refEventRecord" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } } } } @@ -107,6 +118,40 @@ "type": "integer" } } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } } } } diff --git a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/listRecords.json b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/listRecords.json index d0046b0..6b49e21 100644 --- a/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/listRecords.json +++ b/apps/atmo-rsvp/lexicons/generated/rsvp/atmo/rsvp/listRecords.json @@ -22,6 +22,10 @@ "format": "at-identifier", "description": "Filter by an indexed DID or cached handle" }, + "profiles": { + "type": "boolean", + "description": "Include indexed profile and identity information" + }, "createdAtMin": { "type": "string", "description": "Minimum value for createdAt" @@ -78,6 +82,13 @@ }, "cursor": { "type": "string" + }, + "profiles": { + "type": "array", + "items": { + "type": "ref", + "ref": "#profileEntry" + } } } } @@ -165,6 +176,40 @@ "type": "integer" } } + }, + "profileEntry": { + "type": "object", + "required": [ + "did" + ], + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "handle": { + "type": "string" + }, + "uri": { + "type": "string", + "format": "at-uri" + }, + "cid": { + "type": "string", + "format": "cid" + }, + "value": { + "type": "ref", + "ref": "app.bsky.actor.profile#main" + }, + "collection": { + "type": "string", + "format": "nsid" + }, + "rkey": { + "type": "string" + } + } } } } diff --git a/apps/atmo-rsvp/lexicons/pulled/README.md b/apps/atmo-rsvp/lexicons/pulled/README.md index b77ef3e..cf93039 100644 --- a/apps/atmo-rsvp/lexicons/pulled/README.md +++ b/apps/atmo-rsvp/lexicons/pulled/README.md @@ -2,4 +2,4 @@ this directory contains lexicon documents pulled from the following sources: -- atproto (nsids: com.atproto.repo.strongRef, community.lexicon.calendar.event, community.lexicon.calendar.rsvp, community.lexicon.location.address, community.lexicon.location.fsq, community.lexicon.location.geo, community.lexicon.location.hthree) +- atproto (nsids: app.bsky.actor.profile, app.bsky.graph.follow, com.atproto.label.defs, com.atproto.repo.strongRef, community.lexicon.calendar.event, community.lexicon.calendar.rsvp, community.lexicon.location.address, community.lexicon.location.fsq, community.lexicon.location.geo, community.lexicon.location.hthree) diff --git a/apps/atmo-rsvp/lexicons/pulled/app/bsky/actor/profile.json b/apps/atmo-rsvp/lexicons/pulled/app/bsky/actor/profile.json new file mode 100644 index 0000000..35e72fe --- /dev/null +++ b/apps/atmo-rsvp/lexicons/pulled/app/bsky/actor/profile.json @@ -0,0 +1,75 @@ +{ + "id": "app.bsky.actor.profile", + "defs": { + "main": { + "key": "literal:self", + "type": "record", + "record": { + "type": "object", + "properties": { + "avatar": { + "type": "blob", + "accept": [ + "image/jpeg", + "image/png" + ], + "maxSize": 1000000, + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'" + }, + "banner": { + "type": "blob", + "accept": [ + "image/jpeg", + "image/png" + ], + "maxSize": 1000000, + "description": "Larger horizontal image to display behind profile view." + }, + "labels": { + "refs": [ + "com.atproto.label.defs#selfLabels" + ], + "type": "union", + "description": "Self-label values, specific to the Bluesky application, on the overall account." + }, + "website": { + "type": "string", + "format": "uri" + }, + "pronouns": { + "type": "string", + "maxLength": 200, + "description": "Free-form pronouns text.", + "maxGraphemes": 20 + }, + "createdAt": { + "type": "string", + "format": "datetime" + }, + "pinnedPost": { + "ref": "com.atproto.repo.strongRef", + "type": "ref" + }, + "description": { + "type": "string", + "maxLength": 2560, + "description": "Free-form profile description text.", + "maxGraphemes": 256 + }, + "displayName": { + "type": "string", + "maxLength": 640, + "maxGraphemes": 64 + }, + "joinedViaStarterPack": { + "ref": "com.atproto.repo.strongRef", + "type": "ref" + } + } + }, + "description": "A declaration of a Bluesky account profile." + } + }, + "$type": "com.atproto.lexicon.schema", + "lexicon": 1 +} \ No newline at end of file diff --git a/apps/atmo-rsvp/lexicons/pulled/app/bsky/graph/follow.json b/apps/atmo-rsvp/lexicons/pulled/app/bsky/graph/follow.json new file mode 100644 index 0000000..26ada28 --- /dev/null +++ b/apps/atmo-rsvp/lexicons/pulled/app/bsky/graph/follow.json @@ -0,0 +1,33 @@ +{ + "id": "app.bsky.graph.follow", + "defs": { + "main": { + "key": "tid", + "type": "record", + "record": { + "type": "object", + "required": [ + "createdAt", + "subject" + ], + "properties": { + "via": { + "ref": "com.atproto.repo.strongRef", + "type": "ref" + }, + "subject": { + "type": "string", + "format": "did" + }, + "createdAt": { + "type": "string", + "format": "datetime" + } + } + }, + "description": "Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView." + } + }, + "$type": "com.atproto.lexicon.schema", + "lexicon": 1 +} \ No newline at end of file diff --git a/apps/atmo-rsvp/lexicons/pulled/com/atproto/label/defs.json b/apps/atmo-rsvp/lexicons/pulled/com/atproto/label/defs.json new file mode 100644 index 0000000..b7017ec --- /dev/null +++ b/apps/atmo-rsvp/lexicons/pulled/com/atproto/label/defs.json @@ -0,0 +1,190 @@ +{ + "id": "com.atproto.label.defs", + "defs": { + "label": { + "type": "object", + "required": [ + "cts", + "src", + "uri", + "val" + ], + "properties": { + "cid": { + "type": "string", + "format": "cid", + "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to." + }, + "cts": { + "type": "string", + "format": "datetime", + "description": "Timestamp when this label was created." + }, + "exp": { + "type": "string", + "format": "datetime", + "description": "Timestamp at which this label expires (no longer applies)." + }, + "neg": { + "type": "boolean", + "description": "If true, this is a negation label, overwriting a previous label." + }, + "sig": { + "type": "bytes", + "description": "Signature of dag-cbor encoded label." + }, + "src": { + "type": "string", + "format": "did", + "description": "DID of the actor who created this label." + }, + "uri": { + "type": "string", + "format": "uri", + "description": "AT URI of the record, repository (account), or other resource that this label applies to." + }, + "val": { + "type": "string", + "maxLength": 128, + "description": "The short string name of the value or type of this label." + }, + "ver": { + "type": "integer", + "description": "The AT Protocol version of the label object." + } + }, + "description": "Metadata tag on an atproto resource (eg, repo or record)." + }, + "selfLabel": { + "type": "object", + "required": [ + "val" + ], + "properties": { + "val": { + "type": "string", + "maxLength": 128, + "description": "The short string name of the value or type of this label." + } + }, + "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel." + }, + "labelValue": { + "type": "string", + "knownValues": [ + "!hide", + "!no-unauthenticated", + "!warn", + "bot", + "graphic-media", + "nudity", + "porn", + "sexual" + ] + }, + "selfLabels": { + "type": "object", + "required": [ + "values" + ], + "properties": { + "values": { + "type": "array", + "items": { + "ref": "#selfLabel", + "type": "ref" + }, + "maxLength": 10 + } + }, + "description": "Metadata tags on an atproto record, published by the author within the record." + }, + "labelValueDefinition": { + "type": "object", + "required": [ + "blurs", + "identifier", + "locales", + "severity" + ], + "properties": { + "blurs": { + "type": "string", + "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", + "knownValues": [ + "content", + "media", + "none" + ] + }, + "locales": { + "type": "array", + "items": { + "ref": "#labelValueDefinitionStrings", + "type": "ref" + } + }, + "severity": { + "type": "string", + "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", + "knownValues": [ + "alert", + "inform", + "none" + ] + }, + "adultOnly": { + "type": "boolean", + "description": "Does the user need to have adult content enabled in order to configure this label?" + }, + "identifier": { + "type": "string", + "maxLength": 100, + "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", + "maxGraphemes": 100 + }, + "defaultSetting": { + "type": "string", + "default": "warn", + "description": "The default setting for this label.", + "knownValues": [ + "hide", + "ignore", + "warn" + ] + } + }, + "description": "Declares a label value and its expected interpretations and behaviors." + }, + "labelValueDefinitionStrings": { + "type": "object", + "required": [ + "description", + "lang", + "name" + ], + "properties": { + "lang": { + "type": "string", + "format": "language", + "description": "The code of the language these strings are written in." + }, + "name": { + "type": "string", + "maxLength": 640, + "description": "A short human-readable name for the label.", + "maxGraphemes": 64 + }, + "description": { + "type": "string", + "maxLength": 100000, + "description": "A longer description of what the label means and why it might be applied.", + "maxGraphemes": 10000 + } + }, + "description": "Strings which describe the label in the UI, localized into a specific language." + } + }, + "$type": "com.atproto.lexicon.schema", + "lexicon": 1 +} \ No newline at end of file diff --git a/apps/atmo-rsvp/src/contrail.config.ts b/apps/atmo-rsvp/src/contrail.config.ts index 1a6b7b7..78faab4 100644 --- a/apps/atmo-rsvp/src/contrail.config.ts +++ b/apps/atmo-rsvp/src/contrail.config.ts @@ -1,22 +1,19 @@ import type { ContrailConfig } from "@atmo-dev/contrail"; -import { lexicons } from "../lexicons/generated"; export const config: ContrailConfig = { namespace: "rsvp.atmo", - profiles: [], - constellation: false, + profiles: ["app.bsky.actor.profile"], jetstreams: ["wss://jetstream1.us-east.bsky.network"], orderedSource: { source: "jetstream", epoch: "api-atmo-rsvp-primary-2026-08", }, - validation: { - lexicons: lexicons as unknown as NonNullable< - ContrailConfig["validation"] - >["lexicons"], - strict: true, - verifyCid: true, + notify: true, + serviceAuth: { + audience: "did:web:api.atmo.rsvp", + methods: ["getFeed", "notifyOfUpdate"], }, + maintenance: { optimize: true }, collections: { event: { collection: "community.lexicon.calendar.event", @@ -54,5 +51,24 @@ export const config: ContrailConfig = { }, }, }, + profile: { + collection: "app.bsky.actor.profile", + discover: false, + methods: [], + }, + follow: { + collection: "app.bsky.graph.follow", + discover: false, + subjectField: "subject", + methods: [], + }, + }, + feeds: { + network: { + targets: [ + { collection: "event", maxItems: 100 }, + { collection: "rsvp", maxItems: 250 }, + ], + }, }, }; diff --git a/apps/atmo-rsvp/tests/contract.test.ts b/apps/atmo-rsvp/tests/contract.test.ts index c38c80c..f8c305d 100644 --- a/apps/atmo-rsvp/tests/contract.test.ts +++ b/apps/atmo-rsvp/tests/contract.test.ts @@ -4,6 +4,7 @@ import { describePublicService, digestPublicContract, } from "@atmo-dev/contrail"; +import { createSqliteDatabase } from "@atmo-dev/contrail/sqlite"; import { createWorker } from "@atmo-dev/contrail/worker"; import { lexicons } from "../lexicons/generated"; import { config } from "../src/contrail.config"; @@ -12,11 +13,31 @@ const EXPECTED_METHODS = [ "rsvp.atmo.event.getRecord", "rsvp.atmo.event.listRecords", "rsvp.atmo.getCursor", + "rsvp.atmo.getProfile", "rsvp.atmo.rsvp.getRecord", "rsvp.atmo.rsvp.listRecords", ]; +const EXPECTED_PROTECTED_METHODS = [ + { id: "rsvp.atmo.getFeed", type: "query" }, + { id: "rsvp.atmo.notifyOfUpdate", type: "procedure" }, +]; + describe("api.atmo.rsvp public contract", () => { + it("keeps profile and follow projections internal and validation disabled", () => { + expect(config.validation).toBeUndefined(); + expect(config.collections.profile?.methods).toEqual([]); + expect(config.collections.follow).toMatchObject({ + discover: false, + subjectField: "subject", + methods: [], + }); + expect(config.feeds?.network.targets).toEqual([ + { collection: "event", maxItems: 100 }, + { collection: "rsvp", maxItems: 250 }, + ]); + }); + it("advertises the exact generated calendar API", async () => { const service = await describePublicService( config, @@ -27,9 +48,18 @@ describe("api.atmo.rsvp public contract", () => { expect(service.manifest.namespace).toBe("rsvp.atmo"); expect(service.manifest.methods).toEqual(EXPECTED_METHODS); expect(service.manifest.methods).not.toContain("rsvp.atmo.getOverview"); - expect(service.manifest.methods).not.toContain("rsvp.atmo.notifyOfUpdate"); - expect(service.lexicons.map((document) => document.id)).not.toContain( - "com.atproto.label.defs", + expect(service.manifest.serviceAuth).toEqual({ + type: "atproto-service-auth", + audience: "did:web:api.atmo.rsvp", + methods: EXPECTED_PROTECTED_METHODS, + }); + expect(service.lexicons.map((document) => document.id)).toEqual( + expect.arrayContaining([ + "app.bsky.actor.profile", + "app.bsky.graph.follow", + "rsvp.atmo.getFeed", + "rsvp.atmo.notifyOfUpdate", + ]), ); expect( await digestPublicContract(contractFromManifest(service.manifest)), @@ -47,4 +77,42 @@ describe("api.atmo.rsvp public contract", () => { }), ).not.toThrow(); }); + + it("publishes its service DID and permits browser auth headers", async () => { + const worker = createWorker(config, { + lexicons, + publicService: { endpoint: "https://api.atmo.rsvp" }, + }); + const env = { DB: createSqliteDatabase(":memory:") }; + const did = await worker.fetch( + new Request("https://api.atmo.rsvp/.well-known/did.json"), + env, + ); + expect(did.status).toBe(200); + expect(await did.json()).toMatchObject({ + id: "did:web:api.atmo.rsvp", + service: [ + { + id: "did:web:api.atmo.rsvp#contrail", + serviceEndpoint: "https://api.atmo.rsvp", + }, + ], + }); + + const preflight = await worker.fetch( + new Request("https://api.atmo.rsvp/xrpc/rsvp.atmo.getFeed", { + method: "OPTIONS", + headers: { + origin: "https://client.example", + "access-control-request-method": "GET", + "access-control-request-headers": "authorization", + }, + }), + env, + ); + expect(preflight.status).toBe(204); + expect(preflight.headers.get("access-control-allow-headers")).toContain( + "Authorization", + ); + }); }); diff --git a/apps/atmo-rsvp/wrangler.jsonc b/apps/atmo-rsvp/wrangler.jsonc index 0f3a477..9412581 100644 --- a/apps/atmo-rsvp/wrangler.jsonc +++ b/apps/atmo-rsvp/wrangler.jsonc @@ -15,8 +15,8 @@ "d1_databases": [ { "binding": "DB", - "database_name": "atmo-rsvp-contrail", - "database_id": "8a483108-4614-41c4-b9db-86b7a2ea27ca" + "database_name": "atmo-rsvp-contrail-g20260807", + "database_id": "e89f629a-17a8-4fd3-b3c0-a4c6c5ff3bf1" } ], "triggers": {