@clioplaylists/clio 0.1.0

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 (170) hide show
  1. package/dist/.env +7 -0
  2. package/dist/api/com/clioplaylists/alpha/actor/getProfile.js +37 -0
  3. package/dist/api/com/clioplaylists/alpha/feed/getSongs.js +37 -0
  4. package/dist/api/health.js +32 -0
  5. package/dist/api/index.js +43 -0
  6. package/dist/api/util.js +17 -0
  7. package/dist/auth-verifier.js +473 -0
  8. package/dist/client.js +40 -0
  9. package/dist/config.js +65 -0
  10. package/dist/context.js +19 -0
  11. package/dist/dataplane/bsync/index.js +150 -0
  12. package/dist/dataplane/client.js +165 -0
  13. package/dist/dataplane/index.js +18 -0
  14. package/dist/dataplane/server/background.js +51 -0
  15. package/dist/dataplane/server/db/database-schema.js +2 -0
  16. package/dist/dataplane/server/db/db.js +228 -0
  17. package/dist/dataplane/server/db/index.js +17 -0
  18. package/dist/dataplane/server/db/migrations/20230309T045948368Z-init.js +117 -0
  19. package/dist/dataplane/server/db/migrations/20230420T211446071Z-did-cache.js +15 -0
  20. package/dist/dataplane/server/db/migrations/index.js +41 -0
  21. package/dist/dataplane/server/db/migrations/provider.js +31 -0
  22. package/dist/dataplane/server/db/pagination.js +144 -0
  23. package/dist/dataplane/server/db/tables/actor-sync.js +4 -0
  24. package/dist/dataplane/server/db/tables/actor.js +4 -0
  25. package/dist/dataplane/server/db/tables/artist-list-item.js +4 -0
  26. package/dist/dataplane/server/db/tables/artist.js +4 -0
  27. package/dist/dataplane/server/db/tables/playlist-idea.js +4 -0
  28. package/dist/dataplane/server/db/tables/playlist-item.js +4 -0
  29. package/dist/dataplane/server/db/tables/playlist.js +4 -0
  30. package/dist/dataplane/server/db/tables/profile.js +4 -0
  31. package/dist/dataplane/server/db/tables/record.js +4 -0
  32. package/dist/dataplane/server/db/tables/song.js +4 -0
  33. package/dist/dataplane/server/db/types.js +2 -0
  34. package/dist/dataplane/server/db/util.js +48 -0
  35. package/dist/dataplane/server/index.js +52 -0
  36. package/dist/dataplane/server/indexing/index.js +321 -0
  37. package/dist/dataplane/server/indexing/plugins/playlist-idea.js +163 -0
  38. package/dist/dataplane/server/indexing/plugins/profile.js +81 -0
  39. package/dist/dataplane/server/indexing/processor.js +90 -0
  40. package/dist/dataplane/server/routes/blocks.js +95 -0
  41. package/dist/dataplane/server/routes/feed-gens.js +56 -0
  42. package/dist/dataplane/server/routes/feeds.js +128 -0
  43. package/dist/dataplane/server/routes/follows.js +122 -0
  44. package/dist/dataplane/server/routes/identity.js +56 -0
  45. package/dist/dataplane/server/routes/index.js +19 -0
  46. package/dist/dataplane/server/routes/interactions.js +111 -0
  47. package/dist/dataplane/server/routes/labels.js +73 -0
  48. package/dist/dataplane/server/routes/likes.js +76 -0
  49. package/dist/dataplane/server/routes/lists.js +77 -0
  50. package/dist/dataplane/server/routes/moderation.js +92 -0
  51. package/dist/dataplane/server/routes/mutes.js +166 -0
  52. package/dist/dataplane/server/routes/notifs.js +137 -0
  53. package/dist/dataplane/server/routes/posts.js +19 -0
  54. package/dist/dataplane/server/routes/profile.js +61 -0
  55. package/dist/dataplane/server/routes/quotes.js +26 -0
  56. package/dist/dataplane/server/routes/records.js +88 -0
  57. package/dist/dataplane/server/routes/relationships.js +157 -0
  58. package/dist/dataplane/server/routes/reposts.js +59 -0
  59. package/dist/dataplane/server/routes/search.js +70 -0
  60. package/dist/dataplane/server/routes/starter-packs.js +24 -0
  61. package/dist/dataplane/server/routes/suggestions.js +134 -0
  62. package/dist/dataplane/server/routes/sync.js +14 -0
  63. package/dist/dataplane/server/routes/threads.js +31 -0
  64. package/dist/dataplane/server/subscription.js +114 -0
  65. package/dist/dataplane/server/util.js +117 -0
  66. package/dist/error.js +14 -0
  67. package/dist/index.js +115 -0
  68. package/dist/lexicons/index.js +638 -0
  69. package/dist/lexicons/lexicons.js +4551 -0
  70. package/dist/lexicons/types/com/atproto/admin/defs.js +54 -0
  71. package/dist/lexicons/types/com/atproto/admin/deleteAccount.js +2 -0
  72. package/dist/lexicons/types/com/atproto/admin/disableAccountInvites.js +2 -0
  73. package/dist/lexicons/types/com/atproto/admin/disableInviteCodes.js +2 -0
  74. package/dist/lexicons/types/com/atproto/admin/enableAccountInvites.js +2 -0
  75. package/dist/lexicons/types/com/atproto/admin/getAccountInfo.js +2 -0
  76. package/dist/lexicons/types/com/atproto/admin/getAccountInfos.js +2 -0
  77. package/dist/lexicons/types/com/atproto/admin/getInviteCodes.js +2 -0
  78. package/dist/lexicons/types/com/atproto/admin/getSubjectStatus.js +2 -0
  79. package/dist/lexicons/types/com/atproto/admin/searchAccounts.js +2 -0
  80. package/dist/lexicons/types/com/atproto/admin/sendEmail.js +2 -0
  81. package/dist/lexicons/types/com/atproto/admin/updateAccountEmail.js +2 -0
  82. package/dist/lexicons/types/com/atproto/admin/updateAccountHandle.js +2 -0
  83. package/dist/lexicons/types/com/atproto/admin/updateAccountPassword.js +2 -0
  84. package/dist/lexicons/types/com/atproto/admin/updateSubjectStatus.js +2 -0
  85. package/dist/lexicons/types/com/atproto/identity/defs.js +14 -0
  86. package/dist/lexicons/types/com/atproto/identity/getRecommendedDidCredentials.js +2 -0
  87. package/dist/lexicons/types/com/atproto/identity/refreshIdentity.js +2 -0
  88. package/dist/lexicons/types/com/atproto/identity/requestPlcOperationSignature.js +2 -0
  89. package/dist/lexicons/types/com/atproto/identity/resolveDid.js +2 -0
  90. package/dist/lexicons/types/com/atproto/identity/resolveHandle.js +2 -0
  91. package/dist/lexicons/types/com/atproto/identity/resolveIdentity.js +2 -0
  92. package/dist/lexicons/types/com/atproto/identity/signPlcOperation.js +2 -0
  93. package/dist/lexicons/types/com/atproto/identity/submitPlcOperation.js +2 -0
  94. package/dist/lexicons/types/com/atproto/identity/updateHandle.js +2 -0
  95. package/dist/lexicons/types/com/atproto/label/defs.js +54 -0
  96. package/dist/lexicons/types/com/atproto/label/queryLabels.js +2 -0
  97. package/dist/lexicons/types/com/atproto/label/subscribeLabels.js +24 -0
  98. package/dist/lexicons/types/com/atproto/lexicon/schema.js +15 -0
  99. package/dist/lexicons/types/com/atproto/moderation/createReport.js +2 -0
  100. package/dist/lexicons/types/com/atproto/moderation/defs.js +20 -0
  101. package/dist/lexicons/types/com/atproto/repo/applyWrites.js +64 -0
  102. package/dist/lexicons/types/com/atproto/repo/createRecord.js +2 -0
  103. package/dist/lexicons/types/com/atproto/repo/defs.js +14 -0
  104. package/dist/lexicons/types/com/atproto/repo/deleteRecord.js +2 -0
  105. package/dist/lexicons/types/com/atproto/repo/describeRepo.js +2 -0
  106. package/dist/lexicons/types/com/atproto/repo/getRecord.js +2 -0
  107. package/dist/lexicons/types/com/atproto/repo/importRepo.js +2 -0
  108. package/dist/lexicons/types/com/atproto/repo/listMissingBlobs.js +14 -0
  109. package/dist/lexicons/types/com/atproto/repo/listRecords.js +14 -0
  110. package/dist/lexicons/types/com/atproto/repo/putRecord.js +2 -0
  111. package/dist/lexicons/types/com/atproto/repo/strongRef.js +15 -0
  112. package/dist/lexicons/types/com/atproto/repo/uploadBlob.js +2 -0
  113. package/dist/lexicons/types/com/atproto/server/activateAccount.js +2 -0
  114. package/dist/lexicons/types/com/atproto/server/checkAccountStatus.js +2 -0
  115. package/dist/lexicons/types/com/atproto/server/confirmEmail.js +2 -0
  116. package/dist/lexicons/types/com/atproto/server/createAccount.js +2 -0
  117. package/dist/lexicons/types/com/atproto/server/createAppPassword.js +14 -0
  118. package/dist/lexicons/types/com/atproto/server/createInviteCode.js +2 -0
  119. package/dist/lexicons/types/com/atproto/server/createInviteCodes.js +14 -0
  120. package/dist/lexicons/types/com/atproto/server/createSession.js +2 -0
  121. package/dist/lexicons/types/com/atproto/server/deactivateAccount.js +2 -0
  122. package/dist/lexicons/types/com/atproto/server/defs.js +24 -0
  123. package/dist/lexicons/types/com/atproto/server/deleteAccount.js +2 -0
  124. package/dist/lexicons/types/com/atproto/server/deleteSession.js +2 -0
  125. package/dist/lexicons/types/com/atproto/server/describeServer.js +24 -0
  126. package/dist/lexicons/types/com/atproto/server/getAccountInviteCodes.js +2 -0
  127. package/dist/lexicons/types/com/atproto/server/getServiceAuth.js +2 -0
  128. package/dist/lexicons/types/com/atproto/server/getSession.js +2 -0
  129. package/dist/lexicons/types/com/atproto/server/listAppPasswords.js +14 -0
  130. package/dist/lexicons/types/com/atproto/server/refreshSession.js +2 -0
  131. package/dist/lexicons/types/com/atproto/server/requestAccountDelete.js +2 -0
  132. package/dist/lexicons/types/com/atproto/server/requestEmailConfirmation.js +2 -0
  133. package/dist/lexicons/types/com/atproto/server/requestEmailUpdate.js +2 -0
  134. package/dist/lexicons/types/com/atproto/server/requestPasswordReset.js +2 -0
  135. package/dist/lexicons/types/com/atproto/server/reserveSigningKey.js +2 -0
  136. package/dist/lexicons/types/com/atproto/server/resetPassword.js +2 -0
  137. package/dist/lexicons/types/com/atproto/server/revokeAppPassword.js +2 -0
  138. package/dist/lexicons/types/com/atproto/server/updateEmail.js +2 -0
  139. package/dist/lexicons/types/com/atproto/sync/getBlob.js +2 -0
  140. package/dist/lexicons/types/com/atproto/sync/getBlocks.js +2 -0
  141. package/dist/lexicons/types/com/atproto/sync/getCheckout.js +2 -0
  142. package/dist/lexicons/types/com/atproto/sync/getHead.js +2 -0
  143. package/dist/lexicons/types/com/atproto/sync/getLatestCommit.js +2 -0
  144. package/dist/lexicons/types/com/atproto/sync/getRecord.js +2 -0
  145. package/dist/lexicons/types/com/atproto/sync/getRepo.js +2 -0
  146. package/dist/lexicons/types/com/atproto/sync/getRepoStatus.js +2 -0
  147. package/dist/lexicons/types/com/atproto/sync/listBlobs.js +2 -0
  148. package/dist/lexicons/types/com/atproto/sync/listRepos.js +14 -0
  149. package/dist/lexicons/types/com/atproto/sync/listReposByCollection.js +14 -0
  150. package/dist/lexicons/types/com/atproto/sync/notifyOfUpdate.js +2 -0
  151. package/dist/lexicons/types/com/atproto/sync/requestCrawl.js +2 -0
  152. package/dist/lexicons/types/com/atproto/sync/subscribeRepos.js +64 -0
  153. package/dist/lexicons/types/com/atproto/temp/addReservedHandle.js +2 -0
  154. package/dist/lexicons/types/com/atproto/temp/checkSignupQueue.js +2 -0
  155. package/dist/lexicons/types/com/atproto/temp/fetchLabels.js +2 -0
  156. package/dist/lexicons/types/com/atproto/temp/requestPhoneVerification.js +2 -0
  157. package/dist/lexicons/types/com/clioplaylists/alpha/actor/profile.js +15 -0
  158. package/dist/lexicons/types/com/clioplaylists/alpha/feed/defs.js +24 -0
  159. package/dist/lexicons/types/com/clioplaylists/alpha/feed/getSongs.js +2 -0
  160. package/dist/lexicons/types/com/clioplaylists/alpha/feed/playlistIdea.js +35 -0
  161. package/dist/lexicons/types/com/clioplaylists/alpha/feed/song.js +25 -0
  162. package/dist/lexicons/util.js +13 -0
  163. package/dist/logger.js +26 -0
  164. package/dist/rpc/clio_connect.js +110 -0
  165. package/dist/rpc/clio_pb.js +1365 -0
  166. package/dist/start.js +13 -0
  167. package/dist/util/retry.js +16 -0
  168. package/dist/util/uris.js +7 -0
  169. package/dist/util.js +119 -0
  170. package/package.json +73 -0
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@atproto/common");
4
+ const util_1 = require("../../../util");
5
+ const records_1 = require("./records");
6
+ exports.default = (db) => ({
7
+ async getActors(req) {
8
+ const { dids } = req;
9
+ if (dids.length === 0) {
10
+ return { actors: [] };
11
+ }
12
+ const profileUris = dids.map((did) => `at://${did}/app.bsky.actor.profile/self`);
13
+ const chatDeclarationUris = dids.map((did) => `at://${did}/chat.bsky.actor.declaration/self`);
14
+ const [handlesRes, profiles, chatDeclarations] = await Promise.all([
15
+ db.db.selectFrom('actor').selectAll().execute(),
16
+ (0, records_1.getRecords)(db)({ uris: profileUris }),
17
+ (0, records_1.getRecords)(db)({ uris: chatDeclarationUris }),
18
+ ]);
19
+ const byDid = (0, common_1.keyBy)(handlesRes, 'did');
20
+ const actors = dids.map((did, i) => {
21
+ const row = byDid.get(did);
22
+ const chatDeclaration = (0, util_1.parseRecordBytes)(chatDeclarations.records[i].record);
23
+ return {
24
+ exists: !!row,
25
+ handle: row?.handle ?? undefined,
26
+ profile: profiles.records[i],
27
+ takenDown: !!row?.takedownRef,
28
+ takedownRef: row?.takedownRef || undefined,
29
+ tombstonedAt: undefined, // in current implementation, tombstoned actors are deleted
30
+ allowIncomingChatsFrom: typeof chatDeclaration?.['allowIncoming'] === 'string'
31
+ ? chatDeclaration['allowIncoming']
32
+ : undefined,
33
+ upstreamStatus: row?.upstreamStatus ?? '',
34
+ createdAt: profiles.records[i].createdAt, // @NOTE profile creation date not trusted in production
35
+ };
36
+ });
37
+ return { actors };
38
+ },
39
+ async getDidsByHandles(req) {
40
+ const { handles } = req;
41
+ if (handles.length === 0) {
42
+ return { dids: [] };
43
+ }
44
+ const res = await db.db
45
+ .selectFrom('actor')
46
+ .where('handle', 'in', handles)
47
+ .selectAll()
48
+ .execute();
49
+ const byHandle = (0, common_1.keyBy)(res, 'handle');
50
+ const dids = handles.map((handle) => byHandle.get(handle)?.did ?? '');
51
+ return { dids };
52
+ },
53
+ async updateActorUpstreamStatus(req) {
54
+ const { actorDid, upstreamStatus } = req;
55
+ await db.db
56
+ .updateTable('actor')
57
+ .set({ upstreamStatus })
58
+ .where('did', '=', actorDid)
59
+ .execute();
60
+ },
61
+ });
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pagination_1 = require("../db/pagination");
4
+ exports.default = (db) => ({
5
+ async getQuotesBySubjectSorted(req) {
6
+ const { subject, cursor, limit } = req;
7
+ const { ref } = db.db.dynamic;
8
+ if (!subject?.uri)
9
+ return { uris: [] };
10
+ let builder = db.db
11
+ .selectFrom('quote')
12
+ .where('quote.subject', '=', subject.uri)
13
+ .select(['quote.uri', 'quote.cid', 'quote.sortAt']);
14
+ const keyset = new pagination_1.TimeCidKeyset(ref('quote.sortAt'), ref('quote.cid'));
15
+ builder = (0, pagination_1.paginate)(builder, {
16
+ limit,
17
+ cursor,
18
+ keyset,
19
+ });
20
+ const quotes = await builder.execute();
21
+ return {
22
+ uris: quotes.map((q) => q.uri),
23
+ cursor: keyset.packFromResult(quotes),
24
+ };
25
+ },
26
+ });
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getRecords = void 0;
37
+ const common_1 = require("@atproto/common");
38
+ const syntax_1 = require("@atproto/syntax");
39
+ const protobuf_1 = require("@bufbuild/protobuf");
40
+ const ui8 = __importStar(require("uint8arrays"));
41
+ const lexicons_1 = require("../../../lexicons/lexicons");
42
+ const clio_pb_1 = require("../../../rpc/clio_pb");
43
+ exports.default = (db) => ({
44
+ getProfileRecords: (0, exports.getRecords)(db, lexicons_1.ids.ComClioplaylistsAlphaActorProfile),
45
+ });
46
+ const getRecords = (db, collection) => async (req) => {
47
+ const validUris = collection
48
+ ? req.uris.filter((uri) => new syntax_1.AtUri(uri).collection === collection)
49
+ : req.uris;
50
+ const res = validUris.length
51
+ ? await db.db
52
+ .selectFrom('record')
53
+ .selectAll()
54
+ .where('uri', 'in', validUris)
55
+ .execute()
56
+ : [];
57
+ const byUri = (0, common_1.keyBy)(res, 'uri');
58
+ const records = req.uris.map((uri) => {
59
+ const row = byUri.get(uri);
60
+ const json = row ? row.json : JSON.stringify(null);
61
+ const createdAtRaw = new Date(JSON.parse(json)?.['createdAt']);
62
+ const createdAt = !isNaN(createdAtRaw.getTime())
63
+ ? protobuf_1.Timestamp.fromDate(createdAtRaw)
64
+ : undefined;
65
+ const indexedAt = row?.indexed_at
66
+ ? protobuf_1.Timestamp.fromDate(new Date(row?.indexed_at))
67
+ : undefined;
68
+ const recordBytes = ui8.fromString(json, 'utf8');
69
+ return new clio_pb_1.Record({
70
+ record: recordBytes,
71
+ cid: row?.cid,
72
+ createdAt,
73
+ indexedAt,
74
+ sortedAt: compositeTime(createdAt, indexedAt),
75
+ takenDown: !!row?.takedown_ref,
76
+ takedownRef: row?.takedown_ref ?? undefined,
77
+ });
78
+ });
79
+ return { records };
80
+ };
81
+ exports.getRecords = getRecords;
82
+ const compositeTime = (ts1, ts2) => {
83
+ if (!ts1)
84
+ return ts2;
85
+ if (!ts2)
86
+ return ts1;
87
+ return ts1.toDate() < ts2.toDate() ? ts1 : ts2;
88
+ };
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const kysely_1 = require("kysely");
4
+ const common_1 = require("@atproto/common");
5
+ const util_1 = require("../db/util");
6
+ exports.default = (db) => ({
7
+ async getRelationships(req) {
8
+ const { actorDid, targetDids } = req;
9
+ if (targetDids.length === 0) {
10
+ return { relationships: [] };
11
+ }
12
+ const { ref } = db.db.dynamic;
13
+ const res = await db.db
14
+ .selectFrom('actor')
15
+ .where('did', 'in', targetDids)
16
+ .select([
17
+ 'actor.did',
18
+ db.db
19
+ .selectFrom('mute')
20
+ .where('mute.mutedByDid', '=', actorDid)
21
+ .whereRef('mute.subjectDid', '=', ref('actor.did'))
22
+ .select((0, kysely_1.sql) `${true}`.as('val'))
23
+ .as('muted'),
24
+ db.db
25
+ .selectFrom('list_item')
26
+ .innerJoin('list_mute', 'list_mute.listUri', 'list_item.listUri')
27
+ .where('list_mute.mutedByDid', '=', actorDid)
28
+ .whereRef('list_item.subjectDid', '=', ref('actor.did'))
29
+ .select('list_item.listUri')
30
+ .as('mutedByList'),
31
+ db.db
32
+ .selectFrom('actor_block')
33
+ .where('actor_block.creator', '=', actorDid)
34
+ .whereRef('actor_block.subjectDid', '=', ref('actor.did'))
35
+ .select('uri')
36
+ .as('blocking'),
37
+ db.db
38
+ .selectFrom('actor_block')
39
+ .where('actor_block.subjectDid', '=', actorDid)
40
+ .whereRef('actor_block.creator', '=', ref('actor.did'))
41
+ .select('uri')
42
+ .as('blockedBy'),
43
+ db.db
44
+ .selectFrom('list_item')
45
+ .innerJoin('list_block', 'list_block.subjectUri', 'list_item.listUri')
46
+ .where('list_block.creator', '=', actorDid)
47
+ .whereRef('list_item.subjectDid', '=', ref('actor.did'))
48
+ .select('list_item.listUri')
49
+ .as('blockingByList'),
50
+ db.db
51
+ .selectFrom('list_item')
52
+ .innerJoin('list_block', 'list_block.subjectUri', 'list_item.listUri')
53
+ .where('list_item.subjectDid', '=', actorDid)
54
+ .whereRef('list_block.creator', '=', ref('actor.did'))
55
+ .select('list_item.listUri')
56
+ .as('blockedByList'),
57
+ db.db
58
+ .selectFrom('follow')
59
+ .where('follow.creator', '=', actorDid)
60
+ .whereRef('follow.subjectDid', '=', ref('actor.did'))
61
+ .select('uri')
62
+ .as('following'),
63
+ db.db
64
+ .selectFrom('follow')
65
+ .where('follow.subjectDid', '=', actorDid)
66
+ .whereRef('follow.creator', '=', ref('actor.did'))
67
+ .select('uri')
68
+ .as('followedBy'),
69
+ ])
70
+ .execute();
71
+ const byDid = (0, common_1.keyBy)(res, 'did');
72
+ const relationships = targetDids.map((did) => {
73
+ const row = byDid.get(did);
74
+ return {
75
+ muted: row?.muted ?? false,
76
+ mutedByList: row?.mutedByList ?? '',
77
+ blockedBy: row?.blockedBy ?? '',
78
+ blocking: row?.blocking ?? '',
79
+ blockedByList: row?.blockedByList ?? '',
80
+ blockingByList: row?.blockingByList ?? '',
81
+ following: row?.following ?? '',
82
+ followedBy: row?.followedBy ?? '',
83
+ };
84
+ });
85
+ return { relationships };
86
+ },
87
+ async getBlockExistence(req) {
88
+ const { pairs } = req;
89
+ if (pairs.length === 0) {
90
+ return { exists: [], blocks: [] };
91
+ }
92
+ const { ref } = db.db.dynamic;
93
+ const sourceRef = ref('pair.source');
94
+ const targetRef = ref('pair.target');
95
+ const values = (0, util_1.valuesList)(pairs.map((p) => (0, kysely_1.sql) `${p.a}, ${p.b}`));
96
+ const res = await db.db
97
+ .selectFrom(values.as((0, kysely_1.sql) `pair (source, target)`))
98
+ .select([
99
+ (0, kysely_1.sql) `${sourceRef}`.as('source'),
100
+ (0, kysely_1.sql) `${targetRef}`.as('target'),
101
+ (eb) => eb
102
+ .selectFrom('actor_block')
103
+ .whereRef('actor_block.creator', '=', sourceRef)
104
+ .whereRef('actor_block.subjectDid', '=', targetRef)
105
+ .select('uri')
106
+ .as('blocking'),
107
+ (eb) => eb
108
+ .selectFrom('actor_block')
109
+ .whereRef('actor_block.creator', '=', targetRef)
110
+ .whereRef('actor_block.subjectDid', '=', sourceRef)
111
+ .select('uri')
112
+ .as('blockedBy'),
113
+ (eb) => eb
114
+ .selectFrom('list_item')
115
+ .innerJoin('list_block', 'list_block.subjectUri', 'list_item.listUri')
116
+ .whereRef('list_block.creator', '=', sourceRef)
117
+ .whereRef('list_item.subjectDid', '=', targetRef)
118
+ .select('list_item.listUri')
119
+ .as('blockingByList'),
120
+ (eb) => eb
121
+ .selectFrom('list_item')
122
+ .innerJoin('list_block', 'list_block.subjectUri', 'list_item.listUri')
123
+ .whereRef('list_block.creator', '=', targetRef)
124
+ .whereRef('list_item.subjectDid', '=', sourceRef)
125
+ .select('list_item.listUri')
126
+ .as('blockedByList'),
127
+ ])
128
+ .execute();
129
+ const getKey = (a, b) => [a, b].sort().join(',');
130
+ const lookup = res.reduce((acc, cur) => {
131
+ const key = getKey(cur.source, cur.target);
132
+ return acc.set(key, cur);
133
+ }, new Map());
134
+ return {
135
+ exists: pairs.map((pair) => {
136
+ const item = lookup.get(getKey(pair.a, pair.b));
137
+ if (!item)
138
+ return false;
139
+ return !!(item.blocking ||
140
+ item.blockedBy ||
141
+ item.blockingByList ||
142
+ item.blockedByList);
143
+ }),
144
+ blocks: pairs.map((pair) => {
145
+ const item = lookup.get(getKey(pair.a, pair.b));
146
+ if (!item)
147
+ return {};
148
+ return {
149
+ blockedBy: item.blockedBy || undefined,
150
+ blocking: item.blocking || undefined,
151
+ blockedByList: item.blockedByList || undefined,
152
+ blockingByList: item.blockingByList || undefined,
153
+ };
154
+ }),
155
+ };
156
+ },
157
+ });
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@atproto/common");
4
+ const pagination_1 = require("../db/pagination");
5
+ exports.default = (db) => ({
6
+ async getRepostsBySubject(req) {
7
+ const { subject, cursor, limit } = req;
8
+ const { ref } = db.db.dynamic;
9
+ let builder = db.db
10
+ .selectFrom('repost')
11
+ .where('repost.subject', '=', subject?.uri ?? '')
12
+ .selectAll('repost');
13
+ const keyset = new pagination_1.TimeCidKeyset(ref('repost.sortAt'), ref('repost.cid'));
14
+ builder = (0, pagination_1.paginate)(builder, {
15
+ limit,
16
+ cursor,
17
+ keyset,
18
+ });
19
+ const reposts = await builder.execute();
20
+ return {
21
+ uris: reposts.map((l) => l.uri),
22
+ cursor: keyset.packFromResult(reposts),
23
+ };
24
+ },
25
+ async getRepostsByActorAndSubjects(req) {
26
+ const { actorDid, refs } = req;
27
+ if (refs.length === 0) {
28
+ return { uris: [] };
29
+ }
30
+ const res = await db.db
31
+ .selectFrom('repost')
32
+ .where('creator', '=', actorDid)
33
+ .where('subject', 'in', refs.map(({ uri }) => uri))
34
+ .selectAll()
35
+ .execute();
36
+ const bySubject = (0, common_1.keyBy)(res, 'subject');
37
+ const uris = refs.map(({ uri }) => bySubject.get(uri)?.uri ?? '');
38
+ return { uris };
39
+ },
40
+ async getActorReposts(req) {
41
+ const { actorDid, limit, cursor } = req;
42
+ const { ref } = db.db.dynamic;
43
+ let builder = db.db
44
+ .selectFrom('repost')
45
+ .where('repost.creator', '=', actorDid)
46
+ .selectAll();
47
+ const keyset = new pagination_1.TimeCidKeyset(ref('repost.sortAt'), ref('repost.cid'));
48
+ builder = (0, pagination_1.paginate)(builder, {
49
+ limit,
50
+ cursor,
51
+ keyset,
52
+ });
53
+ const reposts = await builder.execute();
54
+ return {
55
+ uris: reposts.map((l) => l.uri),
56
+ cursor: keyset.packFromResult(reposts),
57
+ };
58
+ },
59
+ });
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pagination_1 = require("../db/pagination");
4
+ exports.default = (db) => ({
5
+ // @TODO actor search endpoints still fall back to search service
6
+ async searchActors(req) {
7
+ const { term, limit, cursor } = req;
8
+ const { ref } = db.db.dynamic;
9
+ let builder = db.db
10
+ .selectFrom('actor')
11
+ .where('actor.handle', 'like', `%${cleanQuery(term)}%`)
12
+ .selectAll();
13
+ const keyset = new pagination_1.IndexedAtDidKeyset(ref('actor.indexedAt'), ref('actor.did'));
14
+ builder = (0, pagination_1.paginate)(builder, {
15
+ limit,
16
+ cursor,
17
+ keyset,
18
+ tryIndex: true,
19
+ });
20
+ const res = await builder.execute();
21
+ return {
22
+ dids: res.map((row) => row.did),
23
+ cursor: keyset.packFromResult(res),
24
+ };
25
+ },
26
+ // @TODO post search endpoint still falls back to search service
27
+ async searchPosts(req) {
28
+ const { term, limit, cursor } = req;
29
+ const { ref } = db.db.dynamic;
30
+ let builder = db.db
31
+ .selectFrom('post')
32
+ .where('post.text', 'like', `%${term}%`)
33
+ .selectAll();
34
+ const keyset = new pagination_1.TimeCidKeyset(ref('post.sortAt'), ref('post.cid'));
35
+ builder = (0, pagination_1.paginate)(builder, {
36
+ limit,
37
+ cursor,
38
+ keyset,
39
+ tryIndex: true,
40
+ });
41
+ const res = await builder.execute();
42
+ return {
43
+ uris: res.map((row) => row.uri),
44
+ cursor: keyset.packFromResult(res),
45
+ };
46
+ },
47
+ async searchStarterPacks(req) {
48
+ const { term, limit, cursor } = req;
49
+ const { ref } = db.db.dynamic;
50
+ let builder = db.db
51
+ .selectFrom('starter_pack')
52
+ .where('starter_pack.name', 'ilike', `%${term}%`)
53
+ .selectAll();
54
+ const keyset = new pagination_1.TimeCidKeyset(ref('starter_pack.sortAt'), ref('starter_pack.cid'));
55
+ builder = (0, pagination_1.paginate)(builder, {
56
+ limit,
57
+ cursor,
58
+ keyset,
59
+ tryIndex: true,
60
+ });
61
+ const res = await builder.execute();
62
+ const cur = keyset.packFromResult(res);
63
+ return {
64
+ uris: res.map((row) => row.uri),
65
+ cursor: cur,
66
+ };
67
+ },
68
+ });
69
+ // Remove leading @ in case a handle is input that way
70
+ const cleanQuery = (query) => query.trim().replace(/^@/g, '');
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pagination_1 = require("../db/pagination");
4
+ exports.default = (db) => ({
5
+ async getActorStarterPacks(req) {
6
+ const { actorDid, limit, cursor } = req;
7
+ const { ref } = db.db.dynamic;
8
+ let builder = db.db
9
+ .selectFrom('starter_pack')
10
+ .selectAll()
11
+ .where('creator', '=', actorDid);
12
+ const keyset = new pagination_1.TimeCidKeyset(ref('starter_pack.sortAt'), ref('starter_pack.cid'));
13
+ builder = (0, pagination_1.paginate)(builder, {
14
+ limit,
15
+ cursor,
16
+ keyset,
17
+ });
18
+ const starterPacks = await builder.execute();
19
+ return {
20
+ uris: starterPacks.map((sp) => sp.uri),
21
+ cursor: keyset.packFromResult(starterPacks),
22
+ };
23
+ },
24
+ });
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const kysely_1 = require("kysely");
4
+ exports.default = (db) => ({
5
+ async getFollowSuggestions(req) {
6
+ const { actorDid, relativeToDid, cursor, limit } = req;
7
+ if (relativeToDid) {
8
+ return getFollowSuggestionsRelativeTo(db, {
9
+ actorDid,
10
+ relativeToDid,
11
+ cursor: cursor || undefined,
12
+ limit: limit || undefined,
13
+ });
14
+ }
15
+ else {
16
+ return getFollowSuggestionsGlobal(db, {
17
+ actorDid,
18
+ cursor: cursor || undefined,
19
+ limit: limit || undefined,
20
+ });
21
+ }
22
+ },
23
+ async getSuggestedEntities() {
24
+ const entities = await db.db
25
+ .selectFrom('tagged_suggestion')
26
+ .selectAll()
27
+ .execute();
28
+ return {
29
+ entities,
30
+ };
31
+ },
32
+ });
33
+ const getFollowSuggestionsGlobal = async (db, input) => {
34
+ const alreadyIncluded = parseCursor(input.cursor);
35
+ const suggestions = await db.db
36
+ .selectFrom('suggested_follow')
37
+ .innerJoin('actor', 'actor.did', 'suggested_follow.did')
38
+ .if(alreadyIncluded.length > 0, (qb) => qb.where('suggested_follow.order', 'not in', alreadyIncluded))
39
+ .selectAll()
40
+ .orderBy('suggested_follow.order', 'asc')
41
+ .execute();
42
+ // always include first two
43
+ const firstTwo = suggestions.filter((row) => row.order === 1 || row.order === 2);
44
+ const rest = suggestions.filter((row) => row.order !== 1 && row.order !== 2);
45
+ const limited = firstTwo.concat(shuffle(rest)).slice(0, input.limit);
46
+ // if the result set ends up getting larger, consider using a seed included in the cursor for for the randomized shuffle
47
+ const cursor = limited.length > 0
48
+ ? limited
49
+ .map((row) => row.order.toString())
50
+ .concat(alreadyIncluded.map((id) => id.toString()))
51
+ .join(':')
52
+ : undefined;
53
+ return {
54
+ dids: limited.map((s) => s.did),
55
+ cursor,
56
+ };
57
+ };
58
+ const getFollowSuggestionsRelativeTo = async (db, input) => {
59
+ if (input.cursor)
60
+ return { dids: [] };
61
+ const limit = input.limit ? Math.min(10, input.limit) : 10;
62
+ const actorsViewerFollows = db.db
63
+ .selectFrom('follow')
64
+ .where('creator', '=', input.actorDid)
65
+ .select('subjectDid');
66
+ const mostLikedAccounts = await db.db
67
+ .selectFrom(db.db
68
+ .selectFrom('like')
69
+ .where('creator', '=', input.relativeToDid)
70
+ .select((0, kysely_1.sql) `split_part(subject, '/', 3)`.as('subjectDid'))
71
+ .orderBy('sortAt', 'desc')
72
+ .limit(1000) // limit to 1000
73
+ .as('likes'))
74
+ .select('likes.subjectDid as did')
75
+ .select((qb) => qb.fn.count('likes.subjectDid').as('count'))
76
+ .where('likes.subjectDid', 'not in', actorsViewerFollows)
77
+ .where('likes.subjectDid', 'not in', [input.actorDid, input.relativeToDid])
78
+ .groupBy('likes.subjectDid')
79
+ .orderBy('count', 'desc')
80
+ .limit(limit)
81
+ .execute();
82
+ const resultDids = mostLikedAccounts.map((a) => ({ did: a.did }));
83
+ if (resultDids.length < limit) {
84
+ // backfill with popular accounts followed by actor
85
+ const mostPopularAccountsActorFollows = await db.db
86
+ .selectFrom('follow')
87
+ .innerJoin('profile_agg', 'follow.subjectDid', 'profile_agg.did')
88
+ .select('follow.subjectDid as did')
89
+ .where('follow.creator', '=', input.actorDid)
90
+ .where('follow.subjectDid', '!=', input.relativeToDid)
91
+ .where('follow.subjectDid', 'not in', actorsViewerFollows)
92
+ .if(resultDids.length > 0, (qb) => qb.where('subjectDid', 'not in', resultDids.map((a) => a.did)))
93
+ .orderBy('profile_agg.followersCount', 'desc')
94
+ .limit(limit)
95
+ .execute();
96
+ resultDids.push(...mostPopularAccountsActorFollows);
97
+ }
98
+ if (resultDids.length < limit) {
99
+ // backfill with suggested_follow table
100
+ const additional = await db.db
101
+ .selectFrom('suggested_follow')
102
+ .where('did', 'not in',
103
+ // exclude any we already have
104
+ resultDids
105
+ .map((a) => a.did)
106
+ .concat([input.actorDid, input.relativeToDid]))
107
+ // and aren't already followed by viewer
108
+ .where('did', 'not in', actorsViewerFollows)
109
+ .selectAll()
110
+ .execute();
111
+ resultDids.push(...additional);
112
+ }
113
+ return { dids: resultDids.map((x) => x.did) };
114
+ };
115
+ const parseCursor = (cursor) => {
116
+ if (!cursor) {
117
+ return [];
118
+ }
119
+ try {
120
+ return cursor
121
+ .split(':')
122
+ .map((id) => parseInt(id, 10))
123
+ .filter((id) => !isNaN(id));
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ };
129
+ const shuffle = (arr) => {
130
+ return arr
131
+ .map((value) => ({ value, sort: Math.random() }))
132
+ .sort((a, b) => a.sort - b.sort)
133
+ .map(({ value }) => value);
134
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = (db) => ({
4
+ async getLatestRev(req) {
5
+ const res = await db.db
6
+ .selectFrom('actor_sync')
7
+ .where('did', '=', req.actorDid)
8
+ .select('repoRev')
9
+ .executeTakeFirst();
10
+ return {
11
+ rev: res?.repoRev ?? undefined,
12
+ };
13
+ },
14
+ });
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const util_1 = require("../util");
4
+ exports.default = (db) => ({
5
+ async getThread(req) {
6
+ const { postUri, above, below } = req;
7
+ const [ancestors, descendents] = await Promise.all([
8
+ (0, util_1.getAncestorsAndSelfQb)(db.db, {
9
+ uri: postUri,
10
+ parentHeight: above,
11
+ })
12
+ .selectFrom('ancestor')
13
+ .selectAll()
14
+ .execute(),
15
+ (0, util_1.getDescendentsQb)(db.db, {
16
+ uri: postUri,
17
+ depth: below,
18
+ })
19
+ .selectFrom('descendent')
20
+ .innerJoin('post', 'post.uri', 'descendent.uri')
21
+ .orderBy('post.sortAt', 'desc')
22
+ .selectAll()
23
+ .execute(),
24
+ ]);
25
+ const uris = [
26
+ ...ancestors.map((p) => p.uri),
27
+ ...descendents.map((p) => p.uri),
28
+ ];
29
+ return { uris };
30
+ },
31
+ });