@atproto/bsky 0.0.210 → 0.0.211

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE.txt +1 -1
  3. package/dist/api/app/bsky/draft/createDraft.d.ts +4 -0
  4. package/dist/api/app/bsky/draft/createDraft.d.ts.map +1 -0
  5. package/dist/api/app/bsky/draft/createDraft.js +38 -0
  6. package/dist/api/app/bsky/draft/createDraft.js.map +1 -0
  7. package/dist/api/app/bsky/draft/deleteDraft.d.ts +4 -0
  8. package/dist/api/app/bsky/draft/deleteDraft.d.ts.map +1 -0
  9. package/dist/api/app/bsky/draft/deleteDraft.js +19 -0
  10. package/dist/api/app/bsky/draft/deleteDraft.js.map +1 -0
  11. package/dist/api/app/bsky/draft/getDrafts.d.ts +4 -0
  12. package/dist/api/app/bsky/draft/getDrafts.d.ts.map +1 -0
  13. package/dist/api/app/bsky/draft/getDrafts.js +36 -0
  14. package/dist/api/app/bsky/draft/getDrafts.js.map +1 -0
  15. package/dist/api/app/bsky/draft/updateDraft.d.ts +4 -0
  16. package/dist/api/app/bsky/draft/updateDraft.d.ts.map +1 -0
  17. package/dist/api/app/bsky/draft/updateDraft.js +24 -0
  18. package/dist/api/app/bsky/draft/updateDraft.js.map +1 -0
  19. package/dist/api/app/bsky/feed/searchPosts.js +3 -3
  20. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  21. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts.map +1 -1
  22. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js +9 -8
  23. package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js.map +1 -1
  24. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +1 -1
  25. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
  26. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.d.ts.map +1 -1
  27. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js +3 -4
  28. package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js.map +1 -1
  29. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.d.ts.map +1 -1
  30. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js +9 -8
  31. package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js.map +1 -1
  32. package/dist/api/app/bsky/unspecced/getSuggestedUsers.d.ts.map +1 -1
  33. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js +37 -20
  34. package/dist/api/app/bsky/unspecced/getSuggestedUsers.js.map +1 -1
  35. package/dist/api/app/bsky/unspecced/getTrendingTopics.d.ts.map +1 -1
  36. package/dist/api/app/bsky/unspecced/getTrendingTopics.js +6 -2
  37. package/dist/api/app/bsky/unspecced/getTrendingTopics.js.map +1 -1
  38. package/dist/api/app/bsky/unspecced/getTrends.d.ts.map +1 -1
  39. package/dist/api/app/bsky/unspecced/getTrends.js +9 -8
  40. package/dist/api/app/bsky/unspecced/getTrends.js.map +1 -1
  41. package/dist/api/index.d.ts.map +1 -1
  42. package/dist/api/index.js +8 -0
  43. package/dist/api/index.js.map +1 -1
  44. package/dist/config.d.ts +6 -4
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +18 -21
  47. package/dist/config.js.map +1 -1
  48. package/dist/data-plane/bsync/index.js +38 -0
  49. package/dist/data-plane/bsync/index.js.map +1 -1
  50. package/dist/data-plane/server/db/database-schema.d.ts +2 -1
  51. package/dist/data-plane/server/db/database-schema.d.ts.map +1 -1
  52. package/dist/data-plane/server/db/database-schema.js.map +1 -1
  53. package/dist/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.d.ts +4 -0
  54. package/dist/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.d.ts.map +1 -0
  55. package/dist/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.js +25 -0
  56. package/dist/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.js.map +1 -0
  57. package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
  58. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  59. package/dist/data-plane/server/db/migrations/index.js +2 -1
  60. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  61. package/dist/data-plane/server/db/pagination.d.ts +9 -0
  62. package/dist/data-plane/server/db/pagination.d.ts.map +1 -1
  63. package/dist/data-plane/server/db/pagination.js +7 -1
  64. package/dist/data-plane/server/db/pagination.js.map +1 -1
  65. package/dist/data-plane/server/db/tables/draft.d.ts +12 -0
  66. package/dist/data-plane/server/db/tables/draft.d.ts.map +1 -0
  67. package/dist/data-plane/server/db/tables/draft.js +5 -0
  68. package/dist/data-plane/server/db/tables/draft.js.map +1 -0
  69. package/dist/data-plane/server/indexing/index.d.ts +2 -0
  70. package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
  71. package/dist/data-plane/server/indexing/index.js +2 -0
  72. package/dist/data-plane/server/indexing/index.js.map +1 -1
  73. package/dist/data-plane/server/indexing/plugins/germ-declaration.d.ts +7 -0
  74. package/dist/data-plane/server/indexing/plugins/germ-declaration.d.ts.map +1 -0
  75. package/dist/data-plane/server/indexing/plugins/germ-declaration.js +75 -0
  76. package/dist/data-plane/server/indexing/plugins/germ-declaration.js.map +1 -0
  77. package/dist/data-plane/server/routes/drafts.d.ts +6 -0
  78. package/dist/data-plane/server/routes/drafts.d.ts.map +1 -0
  79. package/dist/data-plane/server/routes/drafts.js +30 -0
  80. package/dist/data-plane/server/routes/drafts.js.map +1 -0
  81. package/dist/data-plane/server/routes/index.d.ts.map +1 -1
  82. package/dist/data-plane/server/routes/index.js +2 -0
  83. package/dist/data-plane/server/routes/index.js.map +1 -1
  84. package/dist/data-plane/server/routes/interactions.d.ts.map +1 -1
  85. package/dist/data-plane/server/routes/interactions.js +6 -0
  86. package/dist/data-plane/server/routes/interactions.js.map +1 -1
  87. package/dist/data-plane/server/routes/profile.d.ts.map +1 -1
  88. package/dist/data-plane/server/routes/profile.js +5 -1
  89. package/dist/data-plane/server/routes/profile.js.map +1 -1
  90. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  91. package/dist/data-plane/server/routes/records.js +1 -0
  92. package/dist/data-plane/server/routes/records.js.map +1 -1
  93. package/dist/data-plane/server/util.d.ts +6 -6
  94. package/dist/feature-gates.d.ts +17 -14
  95. package/dist/feature-gates.d.ts.map +1 -1
  96. package/dist/feature-gates.js +67 -35
  97. package/dist/feature-gates.js.map +1 -1
  98. package/dist/hydration/actor.d.ts +5 -0
  99. package/dist/hydration/actor.d.ts.map +1 -1
  100. package/dist/hydration/actor.js +13 -0
  101. package/dist/hydration/actor.js.map +1 -1
  102. package/dist/hydration/hydrator.d.ts +1 -1
  103. package/dist/hydration/hydrator.d.ts.map +1 -1
  104. package/dist/hydration/hydrator.js +6 -1
  105. package/dist/hydration/hydrator.js.map +1 -1
  106. package/dist/index.js +3 -3
  107. package/dist/index.js.map +1 -1
  108. package/dist/lexicon/index.d.ts +18 -0
  109. package/dist/lexicon/index.d.ts.map +1 -1
  110. package/dist/lexicon/index.js +55 -1
  111. package/dist/lexicon/index.js.map +1 -1
  112. package/dist/lexicon/lexicons.d.ts +1156 -278
  113. package/dist/lexicon/lexicons.d.ts.map +1 -1
  114. package/dist/lexicon/lexicons.js +455 -2
  115. package/dist/lexicon/lexicons.js.map +1 -1
  116. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +19 -1
  117. package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
  118. package/dist/lexicon/types/app/bsky/actor/defs.js +18 -0
  119. package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
  120. package/dist/lexicon/types/app/bsky/draft/createDraft.d.ts +27 -0
  121. package/dist/lexicon/types/app/bsky/draft/createDraft.d.ts.map +1 -0
  122. package/dist/lexicon/types/app/bsky/draft/createDraft.js +7 -0
  123. package/dist/lexicon/types/app/bsky/draft/createDraft.js.map +1 -0
  124. package/dist/lexicon/types/app/bsky/draft/defs.d.ts +106 -0
  125. package/dist/lexicon/types/app/bsky/draft/defs.d.ts.map +1 -0
  126. package/dist/lexicon/types/app/bsky/draft/defs.js +97 -0
  127. package/dist/lexicon/types/app/bsky/draft/defs.js.map +1 -0
  128. package/dist/lexicon/types/app/bsky/draft/deleteDraft.d.ts +14 -0
  129. package/dist/lexicon/types/app/bsky/draft/deleteDraft.d.ts.map +1 -0
  130. package/dist/lexicon/types/app/bsky/draft/deleteDraft.js +7 -0
  131. package/dist/lexicon/types/app/bsky/draft/deleteDraft.js.map +1 -0
  132. package/dist/lexicon/types/app/bsky/draft/getDrafts.d.ts +24 -0
  133. package/dist/lexicon/types/app/bsky/draft/getDrafts.d.ts.map +1 -0
  134. package/dist/lexicon/types/app/bsky/draft/getDrafts.js +7 -0
  135. package/dist/lexicon/types/app/bsky/draft/getDrafts.js.map +1 -0
  136. package/dist/lexicon/types/app/bsky/draft/updateDraft.d.ts +15 -0
  137. package/dist/lexicon/types/app/bsky/draft/updateDraft.d.ts.map +1 -0
  138. package/dist/lexicon/types/app/bsky/draft/updateDraft.js +7 -0
  139. package/dist/lexicon/types/app/bsky/draft/updateDraft.js.map +1 -0
  140. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedUsers.d.ts +1 -1
  141. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedUsers.js.map +1 -1
  142. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedUsersSkeleton.d.ts +1 -1
  143. package/dist/lexicon/types/app/bsky/unspecced/getSuggestedUsersSkeleton.js.map +1 -1
  144. package/dist/lexicon/types/com/germnetwork/declaration.d.ts +24 -0
  145. package/dist/lexicon/types/com/germnetwork/declaration.d.ts.map +1 -0
  146. package/dist/lexicon/types/com/germnetwork/declaration.js +27 -0
  147. package/dist/lexicon/types/com/germnetwork/declaration.js.map +1 -0
  148. package/dist/proto/bsky_connect.d.ts +22 -1
  149. package/dist/proto/bsky_connect.d.ts.map +1 -1
  150. package/dist/proto/bsky_connect.js +21 -0
  151. package/dist/proto/bsky_connect.js.map +1 -1
  152. package/dist/proto/bsky_pb.d.ts +119 -0
  153. package/dist/proto/bsky_pb.d.ts.map +1 -1
  154. package/dist/proto/bsky_pb.js +343 -5
  155. package/dist/proto/bsky_pb.js.map +1 -1
  156. package/dist/stash.d.ts +1 -0
  157. package/dist/stash.d.ts.map +1 -1
  158. package/dist/stash.js +1 -0
  159. package/dist/stash.js.map +1 -1
  160. package/dist/views/index.d.ts.map +1 -1
  161. package/dist/views/index.js +15 -3
  162. package/dist/views/index.js.map +1 -1
  163. package/package.json +10 -10
  164. package/proto/bsky.proto +39 -1
  165. package/src/api/app/bsky/draft/createDraft.ts +46 -0
  166. package/src/api/app/bsky/draft/deleteDraft.ts +19 -0
  167. package/src/api/app/bsky/draft/getDrafts.ts +46 -0
  168. package/src/api/app/bsky/draft/updateDraft.ts +26 -0
  169. package/src/api/app/bsky/feed/searchPosts.ts +4 -4
  170. package/src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts +10 -10
  171. package/src/api/app/bsky/unspecced/getPostThreadV2.ts +2 -2
  172. package/src/api/app/bsky/unspecced/getSuggestedFeeds.ts +4 -5
  173. package/src/api/app/bsky/unspecced/getSuggestedStarterPacks.ts +10 -10
  174. package/src/api/app/bsky/unspecced/getSuggestedUsers.ts +60 -25
  175. package/src/api/app/bsky/unspecced/getTrendingTopics.ts +9 -5
  176. package/src/api/app/bsky/unspecced/getTrends.ts +10 -10
  177. package/src/api/index.ts +8 -0
  178. package/src/config.ts +23 -26
  179. package/src/data-plane/bsync/index.ts +47 -0
  180. package/src/data-plane/server/db/database-schema.ts +3 -1
  181. package/src/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.ts +24 -0
  182. package/src/data-plane/server/db/migrations/index.ts +1 -0
  183. package/src/data-plane/server/db/pagination.ts +8 -0
  184. package/src/data-plane/server/db/tables/draft.ts +11 -0
  185. package/src/data-plane/server/indexing/index.ts +3 -0
  186. package/src/data-plane/server/indexing/plugins/germ-declaration.ts +63 -0
  187. package/src/data-plane/server/routes/drafts.ts +37 -0
  188. package/src/data-plane/server/routes/index.ts +2 -0
  189. package/src/data-plane/server/routes/interactions.ts +6 -0
  190. package/src/data-plane/server/routes/profile.ts +8 -0
  191. package/src/data-plane/server/routes/records.ts +1 -0
  192. package/src/feature-gates.ts +83 -35
  193. package/src/hydration/actor.ts +26 -2
  194. package/src/hydration/hydrator.ts +8 -1
  195. package/src/index.ts +3 -3
  196. package/src/lexicon/index.ts +72 -0
  197. package/src/lexicon/lexicons.ts +465 -2
  198. package/src/lexicon/types/app/bsky/actor/defs.ts +37 -0
  199. package/src/lexicon/types/app/bsky/draft/createDraft.ts +46 -0
  200. package/src/lexicon/types/app/bsky/draft/defs.ts +208 -0
  201. package/src/lexicon/types/app/bsky/draft/deleteDraft.ts +33 -0
  202. package/src/lexicon/types/app/bsky/draft/getDrafts.ts +42 -0
  203. package/src/lexicon/types/app/bsky/draft/updateDraft.ts +34 -0
  204. package/src/lexicon/types/app/bsky/unspecced/getSuggestedUsers.ts +1 -1
  205. package/src/lexicon/types/app/bsky/unspecced/getSuggestedUsersSkeleton.ts +1 -1
  206. package/src/lexicon/types/com/germnetwork/declaration.ts +53 -0
  207. package/src/proto/bsky_connect.ts +22 -1
  208. package/src/proto/bsky_pb.ts +235 -0
  209. package/src/stash.ts +3 -0
  210. package/src/views/index.ts +15 -3
  211. package/tests/views/__snapshots__/profile.test.ts.snap +7 -0
  212. package/tests/views/age-assurance-v2.test.ts +0 -2
  213. package/tests/views/age-assurance.test.ts +0 -2
  214. package/tests/views/drafts.test.ts +293 -0
  215. package/tests/views/profile.test.ts +58 -1
  216. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.210",
3
+ "version": "0.0.211",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -24,6 +24,7 @@
24
24
  "@connectrpc/connect-express": "^1.1.4",
25
25
  "@connectrpc/connect-node": "^1.1.4",
26
26
  "@did-plc/lib": "^0.0.1",
27
+ "@growthbook/growthbook": "^1.6.2",
27
28
  "@hapi/address": "^5.1.1",
28
29
  "@types/http-errors": "^2.0.1",
29
30
  "compression": "^1.7.4",
@@ -45,7 +46,6 @@
45
46
  "pino": "^8.21.0",
46
47
  "pino-http": "^8.2.1",
47
48
  "sharp": "^0.33.5",
48
- "statsig-node": "^5.23.1",
49
49
  "structured-headers": "^1.0.1",
50
50
  "typed-emitter": "^2.1.0",
51
51
  "uint8arrays": "3.0.0",
@@ -53,16 +53,16 @@
53
53
  "zod": "3.23.8",
54
54
  "@atproto-labs/fetch-node": "0.2.0",
55
55
  "@atproto-labs/xrpc-utils": "0.0.24",
56
- "@atproto/api": "^0.18.13",
57
- "@atproto/common": "^0.5.7",
56
+ "@atproto/api": "^0.18.17",
57
+ "@atproto/common": "^0.5.9",
58
58
  "@atproto/crypto": "^0.4.5",
59
59
  "@atproto/did": "^0.2.4",
60
60
  "@atproto/identity": "^0.4.10",
61
+ "@atproto/lexicon": "^0.6.1",
61
62
  "@atproto/repo": "^0.8.12",
62
63
  "@atproto/sync": "^0.1.39",
63
- "@atproto/lexicon": "^0.6.0",
64
- "@atproto/syntax": "^0.4.2",
65
- "@atproto/xrpc-server": "^0.10.8"
64
+ "@atproto/syntax": "^0.4.3",
65
+ "@atproto/xrpc-server": "^0.10.10"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@bufbuild/buf": "^1.28.1",
@@ -78,13 +78,13 @@
78
78
  "jest": "^28.1.2",
79
79
  "ts-node": "^10.8.2",
80
80
  "typescript": "^5.6.3",
81
- "@atproto/api": "^0.18.13",
82
- "@atproto/pds": "^0.4.203",
81
+ "@atproto/api": "^0.18.17",
83
82
  "@atproto/lex-cli": "^0.9.8",
83
+ "@atproto/pds": "^0.4.205",
84
84
  "@atproto/xrpc": "^0.7.7"
85
85
  },
86
86
  "scripts": {
87
- "codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/*",
87
+ "codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/* ../../lexicons/com/germnetwork/*",
88
88
  "build": "tsc --build tsconfig.build.json",
89
89
  "start": "node --enable-source-maps dist/bin.js",
90
90
  "test": "../dev-infra/with-test-redis-and-db.sh jest",
package/proto/bsky.proto CHANGED
@@ -121,6 +121,14 @@ message GetNotificationDeclarationRecordsResponse {
121
121
  repeated Record records = 1;
122
122
  }
123
123
 
124
+ message GetGermDeclarationRecordsRequest {
125
+ repeated string uris = 1;
126
+ }
127
+
128
+ message GetGermDeclarationRecordsResponse {
129
+ repeated Record records = 1;
130
+ }
131
+
124
132
  message GetStatusRecordsRequest {
125
133
  repeated string uris = 1;
126
134
  }
@@ -378,6 +386,7 @@ message GetCountsForUsersResponse {
378
386
  repeated int32 lists = 5;
379
387
  repeated int32 feeds = 6;
380
388
  repeated int32 starter_packs = 7;
389
+ repeated int32 drafts = 8;
381
390
  }
382
391
 
383
392
  message GetStarterPackCountsRequest {
@@ -466,7 +475,6 @@ message GetActorsRequest {
466
475
  }
467
476
 
468
477
  message ActorInfo {
469
- reserved 20; // DO NOT REMOVE — see dataplane repo
470
478
  bool exists = 1;
471
479
  string handle = 2;
472
480
  Record profile = 3;
@@ -488,6 +496,8 @@ message ActorInfo {
488
496
  Record status_record = 17;
489
497
  string allow_activity_subscriptions_from = 18;
490
498
  optional AgeAssuranceStatus age_assurance_status = 19;
499
+ reserved 20; // DO NOT REMOVE - see dataplane repo
500
+ Record germ_record = 21;
491
501
  }
492
502
 
493
503
  message AgeAssuranceStatus {
@@ -1291,6 +1301,29 @@ message GetActorBookmarksResponse {
1291
1301
  string cursor = 2;
1292
1302
  }
1293
1303
 
1304
+ //
1305
+ // Drafts
1306
+ //
1307
+
1308
+
1309
+ message GetActorDraftsRequest {
1310
+ string actor_did = 1;
1311
+ int32 limit = 2;
1312
+ string cursor = 3;
1313
+ }
1314
+
1315
+ message DraftInfo {
1316
+ string key = 1; // stash key
1317
+ google.protobuf.Timestamp created_at = 2;
1318
+ google.protobuf.Timestamp updated_at = 3;
1319
+ bytes payload = 4;
1320
+ }
1321
+
1322
+ message GetActorDraftsResponse {
1323
+ repeated DraftInfo drafts = 1;
1324
+ string cursor = 2;
1325
+ }
1326
+
1294
1327
 
1295
1328
  // Polo-backed Graph Endpoints
1296
1329
 
@@ -1365,6 +1398,7 @@ service Service {
1365
1398
  rpc GetProfileRecords(GetProfileRecordsRequest) returns (GetProfileRecordsResponse);
1366
1399
  rpc GetActorChatDeclarationRecords(GetActorChatDeclarationRecordsRequest) returns (GetActorChatDeclarationRecordsResponse);
1367
1400
  rpc GetNotificationDeclarationRecords(GetNotificationDeclarationRecordsRequest) returns (GetNotificationDeclarationRecordsResponse);
1401
+ rpc GetGermDeclarationRecords(GetGermDeclarationRecordsRequest) returns (GetGermDeclarationRecordsResponse);
1368
1402
  rpc GetStatusRecords(GetStatusRecordsRequest) returns (GetStatusRecordsResponse);
1369
1403
  rpc GetRepostRecords(GetRepostRecordsRequest) returns (GetRepostRecordsResponse);
1370
1404
  rpc GetThreadGateRecords(GetThreadGateRecordsRequest) returns (GetThreadGateRecordsResponse);
@@ -1491,6 +1525,10 @@ service Service {
1491
1525
  // Returns the bookmarks created by the actor.
1492
1526
  rpc GetActorBookmarks(GetActorBookmarksRequest) returns (GetActorBookmarksResponse);
1493
1527
 
1528
+ // Drafts
1529
+ // Returns a page of drafts for a user.
1530
+ rpc GetActorDrafts(GetActorDraftsRequest) returns (GetActorDraftsResponse);
1531
+
1494
1532
  // Identity
1495
1533
  rpc GetIdentityByDid(GetIdentityByDidRequest) returns (GetIdentityByDidResponse);
1496
1534
  rpc GetIdentityByHandle(GetIdentityByHandleRequest) returns (GetIdentityByHandleResponse);
@@ -0,0 +1,46 @@
1
+ import { TID } from '@atproto/common'
2
+ import { InvalidRequestError } from '@atproto/xrpc-server'
3
+ import { AppContext } from '../../../../context'
4
+ import { Server } from '../../../../lexicon'
5
+ import { DraftWithId } from '../../../../lexicon/types/app/bsky/draft/defs'
6
+ import { Namespaces } from '../../../../stash'
7
+
8
+ export default function (server: Server, ctx: AppContext) {
9
+ server.app.bsky.draft.createDraft({
10
+ auth: ctx.authVerifier.standard,
11
+ handler: async ({ input, auth }) => {
12
+ const actorDid = auth.credentials.iss
13
+ const { draft } = input.body
14
+
15
+ const res = await ctx.dataplane.getCountsForUsers({
16
+ dids: [actorDid],
17
+ })
18
+
19
+ const draftsCount = res.drafts[0]
20
+ if (draftsCount >= ctx.cfg.draftsLimit) {
21
+ throw new InvalidRequestError(
22
+ `Drafts limit reached`,
23
+ 'DraftLimitReached',
24
+ )
25
+ }
26
+
27
+ const draftId = TID.nextStr()
28
+ const draftWithId: DraftWithId = {
29
+ id: draftId,
30
+ draft,
31
+ }
32
+
33
+ await ctx.stashClient.create({
34
+ actorDid,
35
+ namespace: Namespaces.AppBskyDraftDefsDraftWithId,
36
+ payload: draftWithId,
37
+ key: draftId,
38
+ })
39
+
40
+ return {
41
+ encoding: 'application/json' as const,
42
+ body: { id: draftId },
43
+ }
44
+ },
45
+ })
46
+ }
@@ -0,0 +1,19 @@
1
+ import { AppContext } from '../../../../context'
2
+ import { Server } from '../../../../lexicon'
3
+ import { Namespaces } from '../../../../stash'
4
+
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.app.bsky.draft.deleteDraft({
7
+ auth: ctx.authVerifier.standard,
8
+ handler: async ({ input, auth }) => {
9
+ const actorDid = auth.credentials.iss
10
+ const { id } = input.body
11
+
12
+ await ctx.stashClient.delete({
13
+ actorDid,
14
+ namespace: Namespaces.AppBskyDraftDefsDraftWithId,
15
+ key: id,
16
+ })
17
+ },
18
+ })
19
+ }
@@ -0,0 +1,46 @@
1
+ import { jsonStringToLex } from '@atproto/lexicon'
2
+ import { AppContext } from '../../../../context'
3
+ import { Server } from '../../../../lexicon'
4
+ import {
5
+ DraftView,
6
+ DraftWithId,
7
+ } from '../../../../lexicon/types/app/bsky/draft/defs'
8
+
9
+ export default function (server: Server, ctx: AppContext) {
10
+ server.app.bsky.draft.getDrafts({
11
+ auth: ctx.authVerifier.standard,
12
+ handler: async ({ params, auth }) => {
13
+ const viewer = auth.credentials.iss
14
+
15
+ const { cursor, drafts } = await ctx.hydrator.dataplane.getActorDrafts({
16
+ actorDid: viewer,
17
+ limit: params.limit,
18
+ cursor: params.cursor,
19
+ })
20
+
21
+ const draftViews = drafts.map((d): DraftView => {
22
+ const draftWithId = jsonStringToLex(
23
+ Buffer.from(d.payload).toString('utf8'),
24
+ ) as DraftWithId
25
+ return {
26
+ id: draftWithId.id,
27
+ draft: draftWithId.draft,
28
+ // The date should always be present, but we avoid required fields on protobuf by convention,
29
+ // so requires a fallback value to please TS.
30
+ createdAt:
31
+ d.createdAt?.toDate().toISOString() ?? new Date(0).toISOString(),
32
+ updatedAt:
33
+ d.updatedAt?.toDate().toISOString() ?? new Date(0).toISOString(),
34
+ }
35
+ })
36
+
37
+ return {
38
+ encoding: 'application/json',
39
+ body: {
40
+ cursor,
41
+ drafts: draftViews,
42
+ },
43
+ }
44
+ },
45
+ })
46
+ }
@@ -0,0 +1,26 @@
1
+ import { AppContext } from '../../../../context'
2
+ import { Server } from '../../../../lexicon'
3
+ import { DraftWithId } from '../../../../lexicon/types/app/bsky/draft/defs'
4
+ import { Namespaces } from '../../../../stash'
5
+
6
+ export default function (server: Server, ctx: AppContext) {
7
+ server.app.bsky.draft.updateDraft({
8
+ auth: ctx.authVerifier.standard,
9
+ handler: async ({ input, auth }) => {
10
+ const actorDid = auth.credentials.iss
11
+ const { draft: draftWithId } = input.body
12
+
13
+ // NOTE: update drafts does not enforce limits, because if it did, we would not allow updating when the limit is reached,
14
+ // which is not the desired behavior.
15
+ // But this means the consumer of the stash operations can't do an upsert behavior on update, and needs instead to drop non-existent
16
+ // drafts. This avoid misusing the update as a create that does not check limits.
17
+
18
+ await ctx.stashClient.update({
19
+ actorDid,
20
+ namespace: Namespaces.AppBskyDraftDefsDraftWithId,
21
+ payload: draftWithId satisfies DraftWithId,
22
+ key: draftWithId.id,
23
+ })
24
+ },
25
+ })
26
+ }
@@ -40,8 +40,8 @@ export default function (server: Server, ctx: AppContext) {
40
40
  labelers,
41
41
  viewer,
42
42
  featureGates: ctx.featureGates.checkGates(
43
- [ctx.featureGates.ids.SearchFilteringExploration],
44
- ctx.featureGates.user({ did: viewer ?? '' }),
43
+ [ctx.featureGates.ids.SearchFilteringExplorationEnable],
44
+ ctx.featureGates.userContext({ did: viewer }),
45
45
  ),
46
46
  })
47
47
  const results = await searchPosts(
@@ -110,7 +110,7 @@ const hydration = async (
110
110
  undefined,
111
111
  {
112
112
  processDynamicTagsForView: params.hydrateCtx.featureGates.get(
113
- FeatureGateID.SearchFilteringExploration,
113
+ FeatureGateID.SearchFilteringExplorationEnable,
114
114
  )
115
115
  ? 'search'
116
116
  : undefined,
@@ -140,7 +140,7 @@ const noBlocksOrTagged = (inputs: RulesFnInput<Context, Params, Skeleton>) => {
140
140
  let tagged = false
141
141
  if (
142
142
  params.hydrateCtx.featureGates.get(
143
- FeatureGateID.SearchFilteringExploration,
143
+ FeatureGateID.SearchFilteringExplorationEnable,
144
144
  )
145
145
  ) {
146
146
  tagged = post.tags.has(ctx.cfg.visibilityTagHide)
@@ -8,7 +8,7 @@ import {
8
8
  mergeManyStates,
9
9
  } from '../../../../hydration/hydrator'
10
10
  import { Server } from '../../../../lexicon'
11
- import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
11
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getOnboardingSuggestedStarterPacks'
12
12
  import {
13
13
  HydrationFnInput,
14
14
  PresentationFnInput,
@@ -37,11 +37,10 @@ export default function (server: Server, ctx: AppContext) {
37
37
  ? req.headers['x-bsky-topics'].join(',')
38
38
  : req.headers['x-bsky-topics'],
39
39
  })
40
- const { ...result } = await getOnboardingSuggestedStarterPacks(
40
+ const result = await getOnboardingSuggestedStarterPacks(
41
41
  {
42
42
  ...params,
43
- viewer: viewer ?? undefined,
44
- hydrateCtx: hydrateCtx.copy({ viewer }),
43
+ hydrateCtx,
45
44
  headers,
46
45
  },
47
46
  ctx,
@@ -61,7 +60,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
61
60
  await ctx.topicsAgent.app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton(
62
61
  {
63
62
  limit: params.limit,
64
- viewer: params.viewer,
63
+ viewer: params.hydrateCtx.viewer ?? undefined,
65
64
  },
66
65
  {
67
66
  headers: params.headers,
@@ -90,8 +89,9 @@ const hydration = async (
90
89
  }
91
90
  dids = dedupeStrs(dids)
92
91
  const pairs: Map<string, string[]> = new Map()
93
- if (params.viewer) {
94
- pairs.set(params.viewer, dids)
92
+ const viewer = params.hydrateCtx.viewer
93
+ if (viewer) {
94
+ pairs.set(viewer, dids)
95
95
  }
96
96
  const [starterPacksState, bidirectionalBlocks] = await Promise.all([
97
97
  ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),
@@ -103,12 +103,12 @@ const hydration = async (
103
103
 
104
104
  const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
105
105
  const { skeleton, params, hydration } = input
106
-
107
- if (!params.viewer) {
106
+ const viewer = params.hydrateCtx.viewer
107
+ if (!viewer) {
108
108
  return skeleton
109
109
  }
110
110
 
111
- const blocks = hydration.bidirectionalBlocks?.get(params.viewer)
111
+ const blocks = hydration.bidirectionalBlocks?.get(viewer)
112
112
  const filteredSkeleton: SkeletonState = {
113
113
  starterPacks: skeleton.starterPacks.filter((uri) => {
114
114
  let aturi: AtUri | undefined
@@ -34,8 +34,8 @@ export default function (server: Server, ctx: AppContext) {
34
34
  includeTakedowns,
35
35
  include3pBlocks,
36
36
  featureGates: ctx.featureGates.checkGates(
37
- [ctx.featureGates.ids.ThreadsV2ReplyRankingExploration],
38
- ctx.featureGates.user({ did: viewer ?? '' }),
37
+ [ctx.featureGates.ids.ThreadsReplyRankingExplorationEnable],
38
+ ctx.featureGates.userContext({ did: viewer }),
39
39
  ),
40
40
  })
41
41
 
@@ -4,7 +4,7 @@ import { InternalServerError } from '@atproto/xrpc-server'
4
4
  import { AppContext } from '../../../../context'
5
5
  import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
6
6
  import { Server } from '../../../../lexicon'
7
- import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
7
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getSuggestedFeeds'
8
8
  import {
9
9
  HydrationFnInput,
10
10
  PresentationFnInput,
@@ -28,11 +28,10 @@ export default function (server: Server, ctx: AppContext) {
28
28
  ? req.headers['x-bsky-topics'].join(',')
29
29
  : req.headers['x-bsky-topics'],
30
30
  })
31
- const { ...result } = await getFeeds(
31
+ const result = await getFeeds(
32
32
  {
33
33
  ...params,
34
- viewer: viewer ?? undefined,
35
- hydrateCtx: hydrateCtx.copy({ viewer }),
34
+ hydrateCtx,
36
35
  headers,
37
36
  },
38
37
  ctx,
@@ -52,7 +51,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
52
51
  await ctx.topicsAgent.app.bsky.unspecced.getSuggestedFeedsSkeleton(
53
52
  {
54
53
  limit: params.limit,
55
- viewer: params.viewer,
54
+ viewer: params.hydrateCtx.viewer ?? undefined,
56
55
  },
57
56
  {
58
57
  headers: params.headers,
@@ -8,7 +8,7 @@ import {
8
8
  mergeManyStates,
9
9
  } from '../../../../hydration/hydrator'
10
10
  import { Server } from '../../../../lexicon'
11
- import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
11
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getSuggestedStarterPacks'
12
12
  import {
13
13
  HydrationFnInput,
14
14
  PresentationFnInput,
@@ -37,11 +37,10 @@ export default function (server: Server, ctx: AppContext) {
37
37
  ? req.headers['x-bsky-topics'].join(',')
38
38
  : req.headers['x-bsky-topics'],
39
39
  })
40
- const { ...result } = await getSuggestedStarterPacks(
40
+ const result = await getSuggestedStarterPacks(
41
41
  {
42
42
  ...params,
43
- viewer: viewer ?? undefined,
44
- hydrateCtx: hydrateCtx.copy({ viewer }),
43
+ hydrateCtx,
45
44
  headers,
46
45
  },
47
46
  ctx,
@@ -61,7 +60,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
61
60
  await ctx.topicsAgent.app.bsky.unspecced.getSuggestedStarterPacksSkeleton(
62
61
  {
63
62
  limit: params.limit,
64
- viewer: params.viewer,
63
+ viewer: params.hydrateCtx.viewer ?? undefined,
65
64
  },
66
65
  {
67
66
  headers: params.headers,
@@ -90,8 +89,9 @@ const hydration = async (
90
89
  }
91
90
  dids = dedupeStrs(dids)
92
91
  const pairs: Map<string, string[]> = new Map()
93
- if (params.viewer) {
94
- pairs.set(params.viewer, dids)
92
+ const viewer = params.hydrateCtx.viewer
93
+ if (viewer) {
94
+ pairs.set(viewer, dids)
95
95
  }
96
96
  const [starterPacksState, bidirectionalBlocks] = await Promise.all([
97
97
  ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),
@@ -103,12 +103,12 @@ const hydration = async (
103
103
 
104
104
  const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
105
105
  const { skeleton, params, hydration } = input
106
-
107
- if (!params.viewer) {
106
+ const viewer = params.hydrateCtx.viewer
107
+ if (!viewer) {
108
108
  return skeleton
109
109
  }
110
110
 
111
- const blocks = hydration.bidirectionalBlocks?.get(params.viewer)
111
+ const blocks = hydration.bidirectionalBlocks?.get(viewer)
112
112
  const filteredSkeleton: SkeletonState = {
113
113
  starterPacks: skeleton.starterPacks.filter((uri) => {
114
114
  let aturi: AtUri | undefined
@@ -2,13 +2,14 @@ import AtpAgent from '@atproto/api'
2
2
  import { dedupeStrs, mapDefined, noUndefinedVals } from '@atproto/common'
3
3
  import { InternalServerError } from '@atproto/xrpc-server'
4
4
  import { AppContext } from '../../../../context'
5
+ import { FeatureGates } from '../../../../feature-gates'
5
6
  import {
6
7
  HydrateCtx,
7
8
  Hydrator,
8
9
  mergeManyStates,
9
10
  } from '../../../../hydration/hydrator'
10
11
  import { Server } from '../../../../lexicon'
11
- import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getTrendingTopics'
12
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/unspecced/getSuggestedUsers'
12
13
  import {
13
14
  HydrationFnInput,
14
15
  PresentationFnInput,
@@ -37,11 +38,10 @@ export default function (server: Server, ctx: AppContext) {
37
38
  ? req.headers['x-bsky-topics'].join(',')
38
39
  : req.headers['x-bsky-topics'],
39
40
  })
40
- const { ...result } = await getSuggestedUsers(
41
+ const result = await getSuggestedUsers(
41
42
  {
42
43
  ...params,
43
- viewer: viewer ?? undefined,
44
- hydrateCtx: hydrateCtx.copy({ viewer }),
44
+ hydrateCtx,
45
45
  headers,
46
46
  },
47
47
  ctx,
@@ -54,25 +54,56 @@ export default function (server: Server, ctx: AppContext) {
54
54
  })
55
55
  }
56
56
 
57
- const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
57
+ // TODO: rename to `skeleton` once we can fully migrate to Discover
58
+ const skeletonFromDiscover = async (
59
+ input: SkeletonFnInput<Context, Params>,
60
+ ) => {
58
61
  const { params, ctx } = input
59
- if (ctx.topicsAgent) {
60
- const res =
61
- await ctx.topicsAgent.app.bsky.unspecced.getSuggestedUsersSkeleton(
62
- {
63
- limit: params.limit,
64
- viewer: params.viewer,
65
- category: params.category,
66
- },
67
- {
68
- headers: params.headers,
69
- },
70
- )
62
+ if (!ctx.suggestionsAgent)
63
+ throw new InternalServerError('Suggestions agent not available')
64
+
65
+ const res =
66
+ await ctx.suggestionsAgent.app.bsky.unspecced.getSuggestedUsersSkeleton(
67
+ {
68
+ limit: params.limit,
69
+ viewer: params.hydrateCtx.viewer ?? undefined,
70
+ category: params.category,
71
+ },
72
+ {
73
+ headers: params.headers,
74
+ },
75
+ )
76
+
77
+ return res.data
78
+ }
71
79
 
72
- return res.data
73
- } else {
80
+ const skeletonFromTopics = async (input: SkeletonFnInput<Context, Params>) => {
81
+ const { params, ctx } = input
82
+ if (!ctx.topicsAgent)
74
83
  throw new InternalServerError('Topics agent not available')
75
- }
84
+
85
+ const res =
86
+ await ctx.topicsAgent.app.bsky.unspecced.getSuggestedUsersSkeleton(
87
+ {
88
+ limit: params.limit,
89
+ viewer: params.hydrateCtx.viewer ?? undefined,
90
+ category: params.category,
91
+ },
92
+ {
93
+ headers: params.headers,
94
+ },
95
+ )
96
+
97
+ return res.data
98
+ }
99
+
100
+ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
101
+ const useDiscover = input.ctx.featureGates.check(
102
+ input.ctx.featureGates.ids.SuggestedUsersDiscoverAgentEnable,
103
+ input.ctx.featureGates.userContext({ did: input.params.hydrateCtx.viewer }),
104
+ )
105
+ const skeletonFn = useDiscover ? skeletonFromDiscover : skeletonFromTopics
106
+ return skeletonFn(input)
76
107
  }
77
108
 
78
109
  const hydration = async (
@@ -81,8 +112,9 @@ const hydration = async (
81
112
  const { ctx, params, skeleton } = input
82
113
  const dids = dedupeStrs(skeleton.dids)
83
114
  const pairs: Map<string, string[]> = new Map()
84
- if (params.viewer) {
85
- pairs.set(params.viewer, dids)
115
+ const viewer = params.hydrateCtx.viewer
116
+ if (viewer) {
117
+ pairs.set(viewer, dids)
86
118
  }
87
119
  const [profilesState, bidirectionalBlocks] = await Promise.all([
88
120
  ctx.hydrator.hydrateProfiles(dids, params.hydrateCtx),
@@ -96,10 +128,11 @@ const noBlocksOrFollows = (
96
128
  input: RulesFnInput<Context, Params, SkeletonState>,
97
129
  ) => {
98
130
  const { ctx, skeleton, params, hydration } = input
99
- if (!params.viewer) {
131
+ const viewer = params.hydrateCtx.viewer
132
+ if (!viewer) {
100
133
  return skeleton
101
134
  }
102
- const blocks = hydration.bidirectionalBlocks?.get(params.viewer)
135
+ const blocks = hydration.bidirectionalBlocks?.get(viewer)
103
136
  return {
104
137
  ...skeleton,
105
138
  dids: skeleton.dids.filter((did) => {
@@ -125,6 +158,8 @@ type Context = {
125
158
  hydrator: Hydrator
126
159
  views: Views
127
160
  topicsAgent: AtpAgent | undefined
161
+ suggestionsAgent: AtpAgent | undefined
162
+ featureGates: FeatureGates
128
163
  }
129
164
 
130
165
  type Params = QueryParams & {
@@ -135,5 +170,5 @@ type Params = QueryParams & {
135
170
 
136
171
  type SkeletonState = {
137
172
  dids: string[]
138
- recId?: number
173
+ recId?: string
139
174
  }
@@ -31,8 +31,12 @@ export default function (server: Server, ctx: AppContext) {
31
31
  const headers = noUndefinedVals({
32
32
  'accept-language': req.headers['accept-language'],
33
33
  })
34
- const { ...result } = await getTrendingTopics(
35
- { ...params, hydrateCtx: hydrateCtx.copy({ viewer }), headers },
34
+ const result = await getTrendingTopics(
35
+ {
36
+ ...params,
37
+ hydrateCtx,
38
+ headers,
39
+ },
36
40
  ctx,
37
41
  )
38
42
  return {
@@ -49,7 +53,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
49
53
  const res = await ctx.topicsAgent.app.bsky.unspecced.getTrendingTopics(
50
54
  {
51
55
  limit: params.limit,
52
- viewer: params.viewer,
56
+ viewer: params.hydrateCtx.viewer ?? undefined,
53
57
  },
54
58
  {
55
59
  headers: params.headers,
@@ -87,8 +91,8 @@ type Context = {
87
91
  topicsAgent: AtpAgent | undefined
88
92
  }
89
93
 
90
- type Params = QueryParams & {
91
- hydrateCtx: HydrateCtx & { viewer: string | null }
94
+ type Params = Omit<QueryParams, 'viewer'> & {
95
+ hydrateCtx: HydrateCtx
92
96
  headers: Record<string, string>
93
97
  }
94
98