···226 println!("jetstream closed the websocket cleanly.");
227 break;
228 }
229- r => eprintln!("jetstream: close result after error: {r:?}"),
000000000000230 }
231- counter!("jetstream_read_fail", "url" => stream.clone(), "reason" => "read error")
232- .increment(1);
233- // if we didn't immediately get ConnectionClosed, we should keep polling read
234- // until we get it.
235- continue;
236 }
237 };
238
···226 println!("jetstream closed the websocket cleanly.");
227 break;
228 }
229+ Err(_) => {
230+ counter!("jetstream_read_fail", "url" => stream.clone(), "reason" => "dirty close").increment(1);
231+ println!("jetstream failed to close the websocket cleanly.");
232+ break;
233+ }
234+ Ok(r) => {
235+ eprintln!("jetstream: close result after error: {r:?}");
236+ counter!("jetstream_read_fail", "url" => stream.clone(), "reason" => "read error")
237+ .increment(1);
238+ // if we didn't immediately get ConnectionClosed, we should keep polling read
239+ // until we get it.
240+ continue;
241+ }
242 }
00000243 }
244 };
245
···1+{
2+ "lexicon": 1,
3+ "id": "com.bad-example.repo.getUriRecord",
4+ "defs": {
5+ "main": {
6+ "type": "query",
7+ "description": "ergonomic complement to com.atproto.repo.getRecord which accepts an at-uri instead of individual repo/collection/rkey params",
8+ "parameters": {
9+ "type": "params",
10+ "required": [
11+ "at_uri"
12+ ],
13+ "properties": {
14+ "at_uri": {
15+ "type": "string",
16+ "format": "at-uri",
17+ "description": "the at-uri of the record (identifier can be a DID or handle)"
18+ },
19+ "cid": {
20+ "type": "string",
21+ "format": "cid",
22+ "description": "optional CID of the version of the record. if not specified, return the most recent version. if specified and a newer version exists, returns 404."
23+ }
24+ }
25+ },
26+ "output": {
27+ "encoding": "application/json",
28+ "schema": {
29+ "type": "object",
30+ "required": [
31+ "uri",
32+ "value"
33+ ],
34+ "properties": {
35+ "uri": {
36+ "type": "string",
37+ "format": "at-uri",
38+ "description": "at-uri for this record"
39+ },
40+ "cid": {
41+ "type": "string",
42+ "format": "cid",
43+ "description": "CID for this exact version of the record"
44+ },
45+ "value": {
46+ "type": "unknown",
47+ "description": "the record itself"
48+ }
49+ }
50+ }
51+ }
52+ }
53+ }
54+}
+1-1
readme.md
···10Tutorials, how-to guides, and client SDK libraries are all in the works for gentler on-ramps, but are not quite ready yet. But don't let that stop you! Hop in the [microcosm discord](https://discord.gg/tcDfe4PGVB), or post questions and tag [@bad-example.com](https://bsky.app/profile/bad-example.com) on Bluesky if you get stuck anywhere.
1112> [!tip]
13-> This repository's primary home is moving to tangled: [@microcosm.blue/microcosm-rs](https://tangled.sh/@microcosm.blue/microcosm-rs). It will continue to be mirrored on [github](https://github.com/at-microcosm/microcosm-rs) for the forseeable future, and it's fine to open issues or pulls in either place!
141516🌌 [Constellation](./constellation/)
···10Tutorials, how-to guides, and client SDK libraries are all in the works for gentler on-ramps, but are not quite ready yet. But don't let that stop you! Hop in the [microcosm discord](https://discord.gg/tcDfe4PGVB), or post questions and tag [@bad-example.com](https://bsky.app/profile/bad-example.com) on Bluesky if you get stuck anywhere.
1112> [!tip]
13+> This repository's primary home is moving to tangled: [@microcosm.blue/microcosm-rs](https://tangled.org/microcosm.blue/microcosm-rs). It will continue to be mirrored on [github](https://github.com/at-microcosm/microcosm-rs) for the forseeable future, and it's fine to open issues or pulls in either place!
141516🌌 [Constellation](./constellation/)
+1-1
slingshot/api-description.md
···90- [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions
9192> [!success]
93-> All microcosm projects are [open source](https://tangled.sh/@bad-example.com/microcosm-links). **You can help sustain Slingshot** and all of microcosm by becoming a [Github sponsor](https://github.com/sponsors/uniphil/) or a [Ko-fi supporter](https://ko-fi.com/bad_example)!
···90- [🎇 Spacedust](https://spacedust.microcosm.blue/), a firehose of all social interactions
9192> [!success]
93+> All microcosm projects are [open source](https://tangled.org/bad-example.com/microcosm-links). **You can help sustain Slingshot** and all of microcosm by becoming a [Github sponsor](https://github.com/sponsors/uniphil/) or a [Ko-fi supporter](https://ko-fi.com/bad_example)!
+2
spacedust/src/error.rs
···30 TooManySourcesWanted,
31 #[error("more wantedSubjectDids were requested than allowed (max 10,000)")]
32 TooManyDidsWanted,
0033 #[error("more wantedSubjects were requested than allowed (max 50,000)")]
34 TooManySubjectsWanted,
35}
···30 TooManySourcesWanted,
31 #[error("more wantedSubjectDids were requested than allowed (max 10,000)")]
32 TooManyDidsWanted,
33+ #[error("more wantedSubjectPrefixes were requested than allowed (max 100)")]
34+ TooManySubjectPrefixesWanted,
35 #[error("more wantedSubjects were requested than allowed (max 50,000)")]
36 TooManySubjectsWanted,
37}
+11-2
spacedust/src/server.rs
···228 #[serde(default)]
229 pub wanted_subjects: HashSet<String>,
230 #[serde(default)]
00231 pub wanted_subject_dids: HashSet<String>,
232 #[serde(default)]
233 pub wanted_sources: HashSet<String>,
···242 ///
243 /// The at-uri must be url-encoded
244 ///
245- /// Pass this parameter multiple times to specify multiple collections, like
246 /// `wantedSubjects=[...]&wantedSubjects=[...]`
247 pub wanted_subjects: String,
0000000248 /// One or more DIDs to receive links about
249 ///
250- /// Pass this parameter multiple times to specify multiple collections
251 pub wanted_subject_dids: String,
252 /// One or more link sources to receive links about
253 ///
···228 #[serde(default)]
229 pub wanted_subjects: HashSet<String>,
230 #[serde(default)]
231+ pub wanted_subject_prefixes: HashSet<String>,
232+ #[serde(default)]
233 pub wanted_subject_dids: HashSet<String>,
234 #[serde(default)]
235 pub wanted_sources: HashSet<String>,
···244 ///
245 /// The at-uri must be url-encoded
246 ///
247+ /// Pass this parameter multiple times to specify multiple subjects, like
248 /// `wantedSubjects=[...]&wantedSubjects=[...]`
249 pub wanted_subjects: String,
250+ /// One or more at-uri, URI, or DID prefixes to receive links about
251+ ///
252+ /// The uri must be url-encoded
253+ ///
254+ /// Pass this parameter multiple times to specify multiple prefixes, like
255+ /// `wantedSubjectPrefixes=[...]&wantedSubjectPrefixes=[...]`
256+ pub wanted_subject_prefixes: String,
257 /// One or more DIDs to receive links about
258 ///
259+ /// Pass this parameter multiple times to specify multiple subjects
260 pub wanted_subject_dids: String,
261 /// One or more link sources to receive links about
262 ///
+10-1
spacedust/src/subscriber.rs
···124 let query = &self.query;
125126 // subject + subject DIDs are logical OR
127- if !(query.wanted_subjects.is_empty() && query.wanted_subject_dids.is_empty()
00128 || query.wanted_subjects.contains(&properties.subject)
0000129 || properties
130 .subject_did
131 .as_ref()
···154 }
155 if opts.wanted_subject_dids.len() > 10_000 {
156 return Err(SubscriberUpdateError::TooManyDidsWanted);
000157 }
158 if opts.wanted_subjects.len() > 50_000 {
159 return Err(SubscriberUpdateError::TooManySubjectsWanted);