birdclaw 0.8.1 → 0.8.3
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.
- package/CHANGELOG.md +25 -0
- package/package.json +2 -4
- package/scripts/browser-perf.mjs +27 -0
- package/src/cli/command-context.ts +17 -0
- package/src/cli/register-analysis.ts +500 -0
- package/src/cli/register-compose.ts +40 -0
- package/src/cli/register-graph.ts +132 -0
- package/src/cli/register-inbox.ts +41 -0
- package/src/cli/register-storage.ts +106 -0
- package/src/cli.ts +30 -750
- package/src/components/AccountSwitcher.tsx +7 -15
- package/src/components/AvatarChip.tsx +1 -1
- package/src/components/AvatarPreload.ts +149 -0
- package/src/components/ConversationThread.tsx +4 -0
- package/src/components/EmbeddedTweetCard.tsx +4 -0
- package/src/components/FloatingPreview.tsx +382 -0
- package/src/components/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +7 -715
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +41 -81
- package/src/components/TimelineCard.tsx +18 -2
- package/src/components/TweetArticleCard.tsx +66 -0
- package/src/components/TweetRichText.tsx +33 -2
- package/src/components/links-controller.ts +162 -0
- package/src/components/links-model.ts +198 -0
- package/src/components/network-map-controller.ts +84 -0
- package/src/components/network-map-model.ts +255 -0
- package/src/components/useDebouncedValue.ts +12 -0
- package/src/components/useTimelineRouteData.ts +142 -170
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -100
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-finder.ts +38 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +77 -182
- package/src/lib/backup.ts +335 -424
- package/src/lib/bird.ts +32 -1
- package/src/lib/blocks-write.ts +30 -26
- package/src/lib/blocks.ts +18 -20
- package/src/lib/database-metrics.ts +88 -0
- package/src/lib/database-migrations.ts +34 -0
- package/src/lib/database-schema.ts +312 -0
- package/src/lib/database-writer.ts +69 -0
- package/src/lib/db.ts +141 -334
- package/src/lib/dm-read-model.ts +533 -0
- package/src/lib/dms-live.ts +34 -97
- package/src/lib/follow-graph.ts +17 -27
- package/src/lib/import-repository.ts +138 -0
- package/src/lib/inbox.ts +2 -1
- package/src/lib/live-sync-engine.ts +209 -0
- package/src/lib/live-transport-gateway.ts +128 -0
- package/src/lib/mention-threads-live.ts +90 -176
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -225
- package/src/lib/moderation-target.ts +15 -4
- package/src/lib/moderation-write.ts +1 -1
- package/src/lib/mutes-write.ts +30 -26
- package/src/lib/openai-response-runtime.ts +251 -0
- package/src/lib/paginated-sync.ts +93 -0
- package/src/lib/period-digest.ts +116 -304
- package/src/lib/profile-analysis.ts +37 -111
- package/src/lib/queries.ts +6 -2380
- package/src/lib/query-actions.ts +437 -0
- package/src/lib/query-client.tsx +47 -0
- package/src/lib/query-read-model-shared.ts +52 -0
- package/src/lib/query-read-models.ts +5 -0
- package/src/lib/query-resource.ts +41 -0
- package/src/lib/query-status.ts +164 -0
- package/src/lib/research.ts +1 -1
- package/src/lib/runtime-services.ts +20 -0
- package/src/lib/search-discussion.ts +75 -279
- package/src/lib/server-runtime-services.ts +30 -0
- package/src/lib/sqlite.ts +53 -14
- package/src/lib/streaming-ingestion.ts +240 -0
- package/src/lib/sync-cache.ts +6 -1
- package/src/lib/sync-plan.ts +175 -0
- package/src/lib/timeline-collections-live.ts +83 -256
- package/src/lib/timeline-live.ts +86 -235
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-render.ts +78 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -166
- package/src/lib/types.ts +8 -0
- package/src/lib/ui.ts +1 -1
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/router.tsx +1 -1
- package/src/routes/__root.tsx +11 -21
- package/src/routes/api/action.tsx +1 -1
- package/src/routes/api/conversation.tsx +1 -1
- package/src/routes/api/query.tsx +32 -26
- package/src/routes/api/status.tsx +6 -2
- package/src/routes/api/sync.tsx +5 -2
- package/src/routes/blocks.tsx +97 -131
- package/src/routes/data-sources.tsx +17 -25
- package/src/routes/dms.tsx +168 -167
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -329
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/vite.config.ts +0 -2
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import {
|
|
3
|
+
getAuthenticatedBirdAccountEffect,
|
|
4
|
+
listBookmarkedTweetsViaBirdEffect,
|
|
5
|
+
listDirectMessagesViaBirdEffect,
|
|
6
|
+
listFollowUsersViaBirdEffect,
|
|
7
|
+
listHomeTimelineViaBirdEffect,
|
|
8
|
+
listLikedTweetsViaBirdEffect,
|
|
9
|
+
listMentionsViaBirdEffect,
|
|
10
|
+
listThreadViaBirdEffect,
|
|
11
|
+
searchTweetsViaBirdEffect,
|
|
12
|
+
} from "./bird";
|
|
13
|
+
import {
|
|
14
|
+
getTransportStatus,
|
|
15
|
+
getTweetByIdEffect,
|
|
16
|
+
listBookmarkedTweetsViaXurl,
|
|
17
|
+
listDirectMessageEventsViaXurlEffect,
|
|
18
|
+
listFollowUsersViaXurl,
|
|
19
|
+
listHomeTimelineViaXurlEffect,
|
|
20
|
+
listLikedTweetsViaXurl,
|
|
21
|
+
listMentionsViaXurl,
|
|
22
|
+
listUserTweets,
|
|
23
|
+
lookupAuthenticatedOAuth2UserEffect,
|
|
24
|
+
lookupAuthenticatedUser,
|
|
25
|
+
lookupUsersByHandles,
|
|
26
|
+
searchRecentByConversationIdEffect,
|
|
27
|
+
searchRecentTweetsEffect,
|
|
28
|
+
} from "./xurl";
|
|
29
|
+
|
|
30
|
+
export interface BirdReadTransport {
|
|
31
|
+
getAuthenticatedAccount: typeof getAuthenticatedBirdAccountEffect;
|
|
32
|
+
listBookmarks: typeof listBookmarkedTweetsViaBirdEffect;
|
|
33
|
+
listDirectMessages: typeof listDirectMessagesViaBirdEffect;
|
|
34
|
+
listFollowUsers: typeof listFollowUsersViaBirdEffect;
|
|
35
|
+
listHomeTimeline: typeof listHomeTimelineViaBirdEffect;
|
|
36
|
+
listLikes: typeof listLikedTweetsViaBirdEffect;
|
|
37
|
+
listMentions: typeof listMentionsViaBirdEffect;
|
|
38
|
+
listThread: typeof listThreadViaBirdEffect;
|
|
39
|
+
searchTweets: typeof searchTweetsViaBirdEffect;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface XurlReadTransport {
|
|
43
|
+
getTransportStatus(
|
|
44
|
+
...args: Parameters<typeof getTransportStatus>
|
|
45
|
+
): Effect.Effect<Awaited<ReturnType<typeof getTransportStatus>>, Error>;
|
|
46
|
+
getTweetById: typeof getTweetByIdEffect;
|
|
47
|
+
listBookmarks(
|
|
48
|
+
...args: Parameters<typeof listBookmarkedTweetsViaXurl>
|
|
49
|
+
): Effect.Effect<
|
|
50
|
+
Awaited<ReturnType<typeof listBookmarkedTweetsViaXurl>>,
|
|
51
|
+
Error
|
|
52
|
+
>;
|
|
53
|
+
listDirectMessages: typeof listDirectMessageEventsViaXurlEffect;
|
|
54
|
+
listFollowUsers(
|
|
55
|
+
...args: Parameters<typeof listFollowUsersViaXurl>
|
|
56
|
+
): Effect.Effect<Awaited<ReturnType<typeof listFollowUsersViaXurl>>, Error>;
|
|
57
|
+
listHomeTimeline: typeof listHomeTimelineViaXurlEffect;
|
|
58
|
+
listLikes(
|
|
59
|
+
...args: Parameters<typeof listLikedTweetsViaXurl>
|
|
60
|
+
): Effect.Effect<Awaited<ReturnType<typeof listLikedTweetsViaXurl>>, Error>;
|
|
61
|
+
listMentions(
|
|
62
|
+
...args: Parameters<typeof listMentionsViaXurl>
|
|
63
|
+
): Effect.Effect<Awaited<ReturnType<typeof listMentionsViaXurl>>, Error>;
|
|
64
|
+
listUserTweets(
|
|
65
|
+
...args: Parameters<typeof listUserTweets>
|
|
66
|
+
): Effect.Effect<Awaited<ReturnType<typeof listUserTweets>>, Error>;
|
|
67
|
+
lookupAuthenticatedOAuth2User: typeof lookupAuthenticatedOAuth2UserEffect;
|
|
68
|
+
lookupAuthenticatedUser(
|
|
69
|
+
...args: Parameters<typeof lookupAuthenticatedUser>
|
|
70
|
+
): Effect.Effect<Awaited<ReturnType<typeof lookupAuthenticatedUser>>, Error>;
|
|
71
|
+
lookupUsersByHandles(
|
|
72
|
+
...args: Parameters<typeof lookupUsersByHandles>
|
|
73
|
+
): Effect.Effect<Awaited<ReturnType<typeof lookupUsersByHandles>>, Error>;
|
|
74
|
+
searchConversation: typeof searchRecentByConversationIdEffect;
|
|
75
|
+
searchRecentTweets: typeof searchRecentTweetsEffect;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface LiveTransportGateway {
|
|
79
|
+
bird: BirdReadTransport;
|
|
80
|
+
xurl: XurlReadTransport;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function fromPromise<T>(run: () => PromiseLike<T>): Effect.Effect<T, Error> {
|
|
84
|
+
return Effect.tryPromise({
|
|
85
|
+
try: run,
|
|
86
|
+
catch: (error) =>
|
|
87
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const liveTransportGateway: LiveTransportGateway = {
|
|
92
|
+
bird: {
|
|
93
|
+
getAuthenticatedAccount: () => getAuthenticatedBirdAccountEffect(),
|
|
94
|
+
listBookmarks: (options) => listBookmarkedTweetsViaBirdEffect(options),
|
|
95
|
+
listDirectMessages: (options) => listDirectMessagesViaBirdEffect(options),
|
|
96
|
+
listFollowUsers: (options) => listFollowUsersViaBirdEffect(options),
|
|
97
|
+
listHomeTimeline: (options) => listHomeTimelineViaBirdEffect(options),
|
|
98
|
+
listLikes: (options) => listLikedTweetsViaBirdEffect(options),
|
|
99
|
+
listMentions: (options) => listMentionsViaBirdEffect(options),
|
|
100
|
+
listThread: (options) => listThreadViaBirdEffect(options),
|
|
101
|
+
searchTweets: (query, options) => searchTweetsViaBirdEffect(query, options),
|
|
102
|
+
},
|
|
103
|
+
xurl: {
|
|
104
|
+
getTransportStatus: (...args) =>
|
|
105
|
+
fromPromise(() => getTransportStatus(...args)),
|
|
106
|
+
getTweetById: (id, options) => getTweetByIdEffect(id, options),
|
|
107
|
+
listBookmarks: (...args) =>
|
|
108
|
+
fromPromise(() => listBookmarkedTweetsViaXurl(...args)),
|
|
109
|
+
listDirectMessages: (options) =>
|
|
110
|
+
listDirectMessageEventsViaXurlEffect(options),
|
|
111
|
+
listFollowUsers: (...args) =>
|
|
112
|
+
fromPromise(() => listFollowUsersViaXurl(...args)),
|
|
113
|
+
listHomeTimeline: (options) => listHomeTimelineViaXurlEffect(options),
|
|
114
|
+
listLikes: (...args) => fromPromise(() => listLikedTweetsViaXurl(...args)),
|
|
115
|
+
listMentions: (...args) => fromPromise(() => listMentionsViaXurl(...args)),
|
|
116
|
+
listUserTweets: (...args) => fromPromise(() => listUserTweets(...args)),
|
|
117
|
+
lookupAuthenticatedOAuth2User: (username) =>
|
|
118
|
+
lookupAuthenticatedOAuth2UserEffect(username),
|
|
119
|
+
lookupAuthenticatedUser: (...args) =>
|
|
120
|
+
fromPromise(() => lookupAuthenticatedUser(...args)),
|
|
121
|
+
lookupUsersByHandles: (...args) =>
|
|
122
|
+
fromPromise(() => lookupUsersByHandles(...args)),
|
|
123
|
+
searchConversation: (conversationId, options) =>
|
|
124
|
+
searchRecentByConversationIdEffect(conversationId, options),
|
|
125
|
+
searchRecentTweets: (query, options) =>
|
|
126
|
+
searchRecentTweetsEffect(query, options),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Database } from "./sqlite";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import {
|
|
3
|
+
import { databaseWriteEffect } from "./database-writer";
|
|
4
4
|
import { getNativeDb } from "./db";
|
|
5
5
|
import { runEffectPromise } from "./effect-runtime";
|
|
6
|
-
import {
|
|
6
|
+
import { liveTransportGateway } from "./live-transport-gateway";
|
|
7
|
+
import { resolveLiveSyncAccount } from "./live-sync-engine";
|
|
8
|
+
import { runSyncPlanEffect } from "./sync-plan";
|
|
7
9
|
import type {
|
|
8
10
|
XurlMentionData,
|
|
9
11
|
XurlMentionsResponse,
|
|
@@ -11,9 +13,7 @@ import type {
|
|
|
11
13
|
XurlMediaItem,
|
|
12
14
|
XurlTweetsResponse,
|
|
13
15
|
} from "./types";
|
|
14
|
-
import {
|
|
15
|
-
import { ensureStubProfileForXUser, upsertProfileFromXUser } from "./x-profile";
|
|
16
|
-
import { getTweetByIdEffect, searchRecentByConversationIdEffect } from "./xurl";
|
|
16
|
+
import { ingestTweetPayload } from "./tweet-repository";
|
|
17
17
|
|
|
18
18
|
const DEFAULT_LIMIT = 30;
|
|
19
19
|
const DEFAULT_DELAY_MS = 1500;
|
|
@@ -106,40 +106,6 @@ function getRemainingThreadTimeoutMs(
|
|
|
106
106
|
}
|
|
107
107
|
return remainingMs;
|
|
108
108
|
}
|
|
109
|
-
function replaceTweetFts(db: Database, tweetId: string, text: string) {
|
|
110
|
-
db.prepare("delete from tweets_fts where tweet_id = ?").run(tweetId);
|
|
111
|
-
db.prepare("insert into tweets_fts (tweet_id, text) values (?, ?)").run(
|
|
112
|
-
tweetId,
|
|
113
|
-
text,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function resolveAccount(db: Database, accountId?: string) {
|
|
118
|
-
const row = accountId
|
|
119
|
-
? (db
|
|
120
|
-
.prepare("select id, handle from accounts where id = ?")
|
|
121
|
-
.get(accountId) as { id: string; handle: string } | undefined)
|
|
122
|
-
: (db
|
|
123
|
-
.prepare(
|
|
124
|
-
`
|
|
125
|
-
select id, handle
|
|
126
|
-
from accounts
|
|
127
|
-
order by is_default desc, created_at asc
|
|
128
|
-
limit 1
|
|
129
|
-
`,
|
|
130
|
-
)
|
|
131
|
-
.get() as { id: string; handle: string } | undefined);
|
|
132
|
-
|
|
133
|
-
if (!row) {
|
|
134
|
-
throw new Error(`Unknown account: ${accountId ?? "default"}`);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
accountId: row.id,
|
|
139
|
-
handle: row.handle.replace(/^@/, "").toLowerCase(),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
109
|
function getReplyToId(tweet: XurlMentionData) {
|
|
144
110
|
return tweet.referenced_tweets?.find((entry) => entry.type === "replied_to")
|
|
145
111
|
?.id;
|
|
@@ -353,84 +319,32 @@ function mergeMentionThreadIntoLocalStore({
|
|
|
353
319
|
const usersById = new Map(
|
|
354
320
|
(payload.includes?.users ?? []).map((user) => [user.id, user]),
|
|
355
321
|
);
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
bookmarked = tweets.bookmarked,
|
|
383
|
-
liked = tweets.liked
|
|
384
|
-
`,
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
db.transaction(() => {
|
|
388
|
-
const seenAt = new Date().toISOString();
|
|
389
|
-
for (const tweet of payload.data) {
|
|
390
|
-
const author =
|
|
391
|
-
usersById.get(tweet.author_id) ??
|
|
392
|
-
({
|
|
393
|
-
id: tweet.author_id,
|
|
394
|
-
username: `user_${tweet.author_id}`,
|
|
395
|
-
name: `user_${tweet.author_id}`,
|
|
396
|
-
} as const);
|
|
397
|
-
const profile = usersById.has(tweet.author_id)
|
|
398
|
-
? upsertProfileFromXUser(db, author)
|
|
399
|
-
: ensureStubProfileForXUser(db, tweet.author_id);
|
|
400
|
-
const handle = author.username.toLowerCase();
|
|
401
|
-
const kind = mentionIds.has(tweet.id)
|
|
402
|
-
? "mention"
|
|
403
|
-
: handle === accountHandle
|
|
404
|
-
? "home"
|
|
405
|
-
: "thread";
|
|
406
|
-
const replyToId = getReplyToId(tweet);
|
|
407
|
-
upsertTweet.run(
|
|
408
|
-
tweet.id,
|
|
409
|
-
accountId,
|
|
410
|
-
profile.profile.id,
|
|
411
|
-
kind,
|
|
412
|
-
tweet.text,
|
|
413
|
-
tweet.created_at,
|
|
414
|
-
replyToId ? 1 : 0,
|
|
415
|
-
replyToId ?? null,
|
|
416
|
-
Number(tweet.public_metrics?.like_count ?? 0),
|
|
417
|
-
countTweetMedia(tweet),
|
|
418
|
-
JSON.stringify(tweet.entities ?? {}),
|
|
419
|
-
buildMediaJsonFromIncludes(tweet, payload.includes?.media),
|
|
420
|
-
);
|
|
421
|
-
if (writeThreadContextEdges) {
|
|
422
|
-
upsertTweetAccountEdge(db, {
|
|
423
|
-
accountId,
|
|
424
|
-
tweetId: tweet.id,
|
|
425
|
-
kind: "thread_context",
|
|
426
|
-
source,
|
|
427
|
-
seenAt,
|
|
428
|
-
rawJson: JSON.stringify(tweet),
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
replaceTweetFts(db, tweet.id, tweet.text);
|
|
432
|
-
}
|
|
433
|
-
})();
|
|
322
|
+
const groups = new Map<"mention" | "home" | "thread", typeof payload.data>([
|
|
323
|
+
["mention", []],
|
|
324
|
+
["home", []],
|
|
325
|
+
["thread", []],
|
|
326
|
+
]);
|
|
327
|
+
for (const tweet of payload.data) {
|
|
328
|
+
const handle = usersById.get(tweet.author_id)?.username.toLowerCase();
|
|
329
|
+
const kind = mentionIds.has(tweet.id)
|
|
330
|
+
? "mention"
|
|
331
|
+
: handle === accountHandle
|
|
332
|
+
? "home"
|
|
333
|
+
: "thread";
|
|
334
|
+
groups.get(kind)?.push(tweet);
|
|
335
|
+
}
|
|
336
|
+
for (const [kind, data] of groups) {
|
|
337
|
+
if (data.length === 0) continue;
|
|
338
|
+
ingestTweetPayload(db, {
|
|
339
|
+
accountId,
|
|
340
|
+
payload: { ...payload, data },
|
|
341
|
+
kind,
|
|
342
|
+
edgeKind: writeThreadContextEdges ? "thread_context" : undefined,
|
|
343
|
+
markRepliesAsReplied: true,
|
|
344
|
+
replaceSecondaryKind: true,
|
|
345
|
+
source,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
434
348
|
}
|
|
435
349
|
|
|
436
350
|
function fetchConversationViaRecentSearchEffect({
|
|
@@ -447,39 +361,29 @@ function fetchConversationViaRecentSearchEffect({
|
|
|
447
361
|
deadlineMs: number;
|
|
448
362
|
}) {
|
|
449
363
|
return Effect.gen(function* () {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
paginationToken: nextToken,
|
|
460
|
-
timeoutMs: yield* trySync(() =>
|
|
461
|
-
getRemainingThreadTimeoutMs(deadlineMs, timeoutMs),
|
|
364
|
+
const result = yield* runSyncPlanEffect({
|
|
365
|
+
fetchPage: ({ cursor }) =>
|
|
366
|
+
trySync(() => getRemainingThreadTimeoutMs(deadlineMs, timeoutMs)).pipe(
|
|
367
|
+
Effect.flatMap((remainingTimeoutMs) =>
|
|
368
|
+
liveTransportGateway.xurl.searchConversation(conversationId, {
|
|
369
|
+
maxResults: MAX_XURL_SEARCH_RESULTS,
|
|
370
|
+
paginationToken: cursor,
|
|
371
|
+
timeoutMs: remainingTimeoutMs,
|
|
372
|
+
}),
|
|
462
373
|
),
|
|
463
|
-
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
} while (
|
|
472
|
-
(all || maxPages !== undefined) &&
|
|
473
|
-
nextToken &&
|
|
474
|
-
(maxPages === undefined || pageCount < maxPages)
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
const payload = mergePayloads(pages);
|
|
374
|
+
),
|
|
375
|
+
getNextCursor: (page) =>
|
|
376
|
+
typeof page.meta?.next_token === "string"
|
|
377
|
+
? page.meta.next_token
|
|
378
|
+
: undefined,
|
|
379
|
+
maxPages: all || maxPages !== undefined ? maxPages : 1,
|
|
380
|
+
});
|
|
381
|
+
const payload = mergePayloads(result.pages);
|
|
478
382
|
const paginationRequested = all || maxPages !== undefined;
|
|
479
383
|
return {
|
|
480
384
|
payload,
|
|
481
|
-
pages:
|
|
482
|
-
truncated: paginationRequested && Boolean(
|
|
385
|
+
pages: result.pages.length,
|
|
386
|
+
truncated: paginationRequested && Boolean(result.nextCursor),
|
|
483
387
|
generalReadTweets: payload.data.length,
|
|
484
388
|
};
|
|
485
389
|
});
|
|
@@ -511,11 +415,14 @@ function fetchParentChainViaXurlEffect({
|
|
|
511
415
|
let shouldUseRawAnchor = Boolean(rawAnchorPayload);
|
|
512
416
|
|
|
513
417
|
if (!nextParentId) {
|
|
514
|
-
const anchorPayload = yield*
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
418
|
+
const anchorPayload = yield* liveTransportGateway.xurl.getTweetById(
|
|
419
|
+
mention.id,
|
|
420
|
+
{
|
|
421
|
+
timeoutMs: yield* trySync(() =>
|
|
422
|
+
getRemainingThreadTimeoutMs(deadlineMs, timeoutMs),
|
|
423
|
+
),
|
|
424
|
+
},
|
|
425
|
+
);
|
|
519
426
|
pages.push(anchorPayload);
|
|
520
427
|
generalReadTweets += anchorPayload.data.length;
|
|
521
428
|
const anchorTweet = anchorPayload.data[0];
|
|
@@ -547,11 +454,14 @@ function fetchParentChainViaXurlEffect({
|
|
|
547
454
|
}
|
|
548
455
|
|
|
549
456
|
fallbackDepth += 1;
|
|
550
|
-
const parentPayload = yield*
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
457
|
+
const parentPayload = yield* liveTransportGateway.xurl.getTweetById(
|
|
458
|
+
nextParentId,
|
|
459
|
+
{
|
|
460
|
+
timeoutMs: yield* trySync(() =>
|
|
461
|
+
getRemainingThreadTimeoutMs(deadlineMs, timeoutMs),
|
|
462
|
+
),
|
|
463
|
+
},
|
|
464
|
+
);
|
|
555
465
|
pages.push(parentPayload);
|
|
556
466
|
generalReadTweets += parentPayload.data.length;
|
|
557
467
|
const parentTweet = parentPayload.data[0];
|
|
@@ -742,7 +652,9 @@ export function syncMentionThreadsEffect({
|
|
|
742
652
|
parseNonNegativeInteger(maxPages, "--max-pages"),
|
|
743
653
|
);
|
|
744
654
|
const db = yield* trySync(() => getNativeDb());
|
|
745
|
-
const resolvedAccount = yield* trySync(() =>
|
|
655
|
+
const resolvedAccount = yield* trySync(() =>
|
|
656
|
+
resolveLiveSyncAccount(db, account),
|
|
657
|
+
);
|
|
746
658
|
const mentions = yield* trySync(() =>
|
|
747
659
|
tweetIds
|
|
748
660
|
? listMentionsByIds(
|
|
@@ -778,22 +690,24 @@ export function syncMentionThreadsEffect({
|
|
|
778
690
|
}
|
|
779
691
|
const fetchEffect: Effect.Effect<ThreadFetchResult, unknown, never> =
|
|
780
692
|
parsedMode === "bird"
|
|
781
|
-
?
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
payload
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
693
|
+
? liveTransportGateway.bird
|
|
694
|
+
.listThread({
|
|
695
|
+
tweetId: mention.id,
|
|
696
|
+
all,
|
|
697
|
+
maxPages: parsedMaxPages,
|
|
698
|
+
timeoutMs: parsedTimeoutMs,
|
|
699
|
+
})
|
|
700
|
+
.pipe(
|
|
701
|
+
Effect.map((payload) => ({
|
|
702
|
+
strategy: "bird" as const,
|
|
703
|
+
payload,
|
|
704
|
+
pages: undefined,
|
|
705
|
+
fallbackDepth: undefined,
|
|
706
|
+
generalReadTweets: 0,
|
|
707
|
+
truncated: undefined,
|
|
708
|
+
warnings: [] as string[],
|
|
709
|
+
})),
|
|
710
|
+
)
|
|
797
711
|
: fetchThreadContextViaXurlEffect({
|
|
798
712
|
mention,
|
|
799
713
|
all,
|
|
@@ -803,11 +717,11 @@ export function syncMentionThreadsEffect({
|
|
|
803
717
|
});
|
|
804
718
|
const fetched = yield* fetchEffect.pipe(
|
|
805
719
|
Effect.flatMap((fetchResult) =>
|
|
806
|
-
|
|
720
|
+
databaseWriteEffect((writeDb) =>
|
|
807
721
|
mergeMentionThreadIntoLocalStore({
|
|
808
|
-
db,
|
|
722
|
+
db: writeDb,
|
|
809
723
|
accountId: resolvedAccount.accountId,
|
|
810
|
-
accountHandle: resolvedAccount.
|
|
724
|
+
accountHandle: resolvedAccount.username.toLowerCase(),
|
|
811
725
|
mentionIds: mentionIdSet,
|
|
812
726
|
payload: fetchResult.payload,
|
|
813
727
|
source: parsedMode,
|