birdclaw 0.9.2 → 0.9.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 +6 -0
- package/dist/cli/birdclaw.js +1227 -1065
- package/dist/client/assets/AvatarChip-C8zELEVt.js +1 -0
- package/dist/client/assets/ProfilePreview-CsiLpFrb.js +1 -0
- package/dist/client/assets/SavedTimelineView-BSnwnujP.js +1 -0
- package/dist/client/assets/SmartTimestamp-BypN4KPq.js +1 -0
- package/dist/client/assets/TimelineRouteFrame-H3D4R0Xu.js +1 -0
- package/dist/client/assets/blocks-WsAOmHsv.js +1 -0
- package/dist/client/assets/{bookmarks-DZIMTKEp.js → bookmarks-B2I0kAWY.js} +1 -1
- package/dist/client/assets/circle-check-BCK_qvXt.js +1 -0
- package/dist/client/assets/{client-stream-contracts-DcKcyZhg.js → client-stream-contracts-D1QUdWfh.js} +1 -1
- package/dist/client/assets/createLucideIcon-CMLcIxOz.js +1 -0
- package/dist/client/assets/data-sources-DI5W9oVq.js +1 -0
- package/dist/client/assets/discuss-t2QxXK0h.js +1 -0
- package/dist/client/assets/dms-DjyXURaJ.js +1 -0
- package/dist/client/assets/inbox-CkHVoykL.js +1 -0
- package/dist/client/assets/index-BR860w88.js +31 -0
- package/dist/client/assets/likes-CenHqxQG.js +1 -0
- package/dist/client/assets/links-lDqy93u8.js +1 -0
- package/dist/client/assets/mapbox-CVPXIYUZ.js +2 -0
- package/dist/client/assets/{mapbox-gl-CPemBIlO.js → mapbox-gl-DRRiLKWb.js} +1 -1
- package/dist/client/assets/{mentions-DBrY-UPg.js → mentions-B6BXntri.js} +1 -1
- package/dist/client/assets/network-map-BCUtBk8G.js +2 -0
- package/dist/client/assets/profile-analyze-YExKd6Lx.js +1 -0
- package/dist/client/assets/{profile-hydration-client-BNe0sUmK.js → profile-hydration-client-BQyC5--e.js} +1 -1
- package/dist/client/assets/profiles._handle-idhd3kYo.js +1 -0
- package/dist/client/assets/query-client-DZ5C7UNo.js +17 -0
- package/dist/client/assets/rate-limits-jGxIfvUD.js +1 -0
- package/dist/client/assets/react-BwGzW7mM.js +1 -0
- package/dist/client/assets/react-dom-DEMAB80t.js +1 -0
- package/dist/client/assets/{repeat-2-xCwHM9hD.js → repeat-2-CQ0lek01.js} +1 -1
- package/dist/client/assets/rolldown-runtime-B-9mzPXz.js +1 -0
- package/dist/client/assets/{routes-Bl-Lw2hN.js → routes-6U4Cc79j.js} +1 -1
- package/dist/client/assets/schemas-CntsNLmN.js +64 -0
- package/dist/client/assets/search-C_LqodNa.js +1 -0
- package/dist/client/assets/{sparkles-CQVlvHsZ.js → sparkles-Gx_BHkDX.js} +1 -1
- package/dist/client/assets/{styles-zihNNCGg.css → styles-DT4Pxoi-.css} +1 -1
- package/dist/client/assets/today-COmNSYaE.js +1 -0
- package/dist/client/assets/{triangle-alert-CBL7s2pf.js → triangle-alert-DJPCW6jQ.js} +1 -1
- package/dist/client/assets/useTimelineRouteData-BpQzU16P.js +1 -0
- package/dist/client/assets/{users-CHbKlqUr.js → users-DI3w8Mol.js} +1 -1
- package/dist/server/assets/{_tanstack-start-manifest_v-CVfs07C8.js → _tanstack-start-manifest_v-mm-2VYBo.js} +35 -35
- package/dist/server/assets/{empty-plugin-adapters-DzrEkdBF.js → empty-plugin-adapters-D9UWiqvJ.js} +1 -1
- package/dist/server/assets/{router-D8AUR_Gz.js → router-B874XqWN.js} +708 -689
- package/dist/server/assets/start-5Z2QO8AU.js +4 -0
- package/dist/server/server.js +50 -3413
- package/package.json +1 -1
- package/dist/client/assets/AvatarChip-BN3b4MhM.js +0 -1
- package/dist/client/assets/ProfilePreview-CHbT0U9a.js +0 -1
- package/dist/client/assets/SavedTimelineView-mGXE9EYJ.js +0 -1
- package/dist/client/assets/SmartTimestamp-DG8XJKUz.js +0 -1
- package/dist/client/assets/TimelineRouteFrame-BmMC7qLj.js +0 -1
- package/dist/client/assets/blocks-CawnB0mN.js +0 -1
- package/dist/client/assets/chunk-C0phanGV.js +0 -1
- package/dist/client/assets/circle-check-C_Vq3ElD.js +0 -1
- package/dist/client/assets/createLucideIcon-JV8isFFR.js +0 -1
- package/dist/client/assets/data-sources-BJUT2qaO.js +0 -1
- package/dist/client/assets/discuss-DMPJVaRk.js +0 -1
- package/dist/client/assets/dms-BfPFG4Qr.js +0 -1
- package/dist/client/assets/inbox-DmeYJ7tt.js +0 -1
- package/dist/client/assets/index-Bqmo4MbK.js +0 -31
- package/dist/client/assets/likes-BxVL-j_C.js +0 -1
- package/dist/client/assets/links-BeA1Wt8F.js +0 -1
- package/dist/client/assets/mapbox-BTSMNSry.js +0 -2
- package/dist/client/assets/network-map-MqbE164r.js +0 -2
- package/dist/client/assets/profile-analyze-DEA75Reh.js +0 -1
- package/dist/client/assets/profiles._handle-K34ofz9U.js +0 -1
- package/dist/client/assets/query-client-DUP1ZzP7.js +0 -17
- package/dist/client/assets/rate-limits-B8Wmpt9r.js +0 -1
- package/dist/client/assets/react-dom-DHl6Ug7F.js +0 -1
- package/dist/client/assets/react-nBylq1U5.js +0 -1
- package/dist/client/assets/schemas-CaALBBpc.js +0 -64
- package/dist/client/assets/search-Dq5kYsxA.js +0 -1
- package/dist/client/assets/today-DFvZ0Ys5.js +0 -1
- package/dist/client/assets/useTimelineRouteData-Cpf-Jqsx.js +0 -1
- package/dist/server/assets/start-FaDOTmto.js +0 -4
package/dist/cli/birdclaw.js
CHANGED
|
@@ -5090,7 +5090,7 @@ function createCommandContext(program2) {
|
|
|
5090
5090
|
|
|
5091
5091
|
// src/lib/period-digest.ts
|
|
5092
5092
|
import { createHash as createHash5 } from "node:crypto";
|
|
5093
|
-
import { Effect as
|
|
5093
|
+
import { Effect as Effect16 } from "effect";
|
|
5094
5094
|
import { z } from "zod";
|
|
5095
5095
|
|
|
5096
5096
|
// src/lib/analysis-runtime.ts
|
|
@@ -6273,10 +6273,7 @@ function getLinkInsights(query = {}) {
|
|
|
6273
6273
|
}
|
|
6274
6274
|
|
|
6275
6275
|
// src/lib/mention-threads-live.ts
|
|
6276
|
-
import { Effect as
|
|
6277
|
-
|
|
6278
|
-
// src/lib/live-transport-gateway.ts
|
|
6279
|
-
import { Effect as Effect10 } from "effect";
|
|
6276
|
+
import { Effect as Effect13 } from "effect";
|
|
6280
6277
|
|
|
6281
6278
|
// src/lib/bird.ts
|
|
6282
6279
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -6386,8 +6383,8 @@ function makeBirdStdoutTempEffect() {
|
|
|
6386
6383
|
({ tempDir }) => Effect8.sync(() => rmSync(tempDir, { recursive: true, force: true }))
|
|
6387
6384
|
);
|
|
6388
6385
|
}
|
|
6389
|
-
|
|
6390
|
-
|
|
6386
|
+
var runBirdJsonCommandEffect = Effect8.fn("bird.runJsonCommand")(
|
|
6387
|
+
(args, timeoutMs) => Effect8.scoped(
|
|
6391
6388
|
Effect8.gen(function* () {
|
|
6392
6389
|
const birdCommand = yield* Effect8.try({
|
|
6393
6390
|
try: () => getBirdCommand(),
|
|
@@ -6414,10 +6411,12 @@ function runBirdJsonCommandEffect(args, timeoutMs) {
|
|
|
6414
6411
|
catch: (error) => error
|
|
6415
6412
|
});
|
|
6416
6413
|
})
|
|
6417
|
-
)
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6414
|
+
)
|
|
6415
|
+
);
|
|
6416
|
+
var runBirdJsonCommandAllowFailureEffect = Effect8.fn(
|
|
6417
|
+
"bird.runJsonCommandAllowFailure"
|
|
6418
|
+
)(
|
|
6419
|
+
(args, timeoutMs) => Effect8.scoped(
|
|
6421
6420
|
Effect8.gen(function* () {
|
|
6422
6421
|
const birdCommand = yield* Effect8.try({
|
|
6423
6422
|
try: () => getBirdCommand(),
|
|
@@ -6450,8 +6449,8 @@ function runBirdJsonCommandAllowFailureEffect(args, timeoutMs) {
|
|
|
6450
6449
|
catch: (error) => error
|
|
6451
6450
|
});
|
|
6452
6451
|
})
|
|
6453
|
-
)
|
|
6454
|
-
|
|
6452
|
+
)
|
|
6453
|
+
);
|
|
6455
6454
|
function runBirdTweetJsonCommandEffect(args, timeoutMs) {
|
|
6456
6455
|
return runBirdJsonCommandEffect([...args, "--json-full"], timeoutMs).pipe(
|
|
6457
6456
|
Effect8.catchAll((error) => {
|
|
@@ -6641,10 +6640,8 @@ function normalizeBirdTweetItemEffect(payload, command) {
|
|
|
6641
6640
|
catch: (error) => error
|
|
6642
6641
|
});
|
|
6643
6642
|
}
|
|
6644
|
-
|
|
6645
|
-
maxResults
|
|
6646
|
-
}) {
|
|
6647
|
-
return Effect8.gen(function* () {
|
|
6643
|
+
var listMentionsViaBirdEffect = Effect8.fn("bird.listMentions")(
|
|
6644
|
+
function* ({ maxResults }) {
|
|
6648
6645
|
const stdout = yield* runBirdTweetJsonCommandEffect([
|
|
6649
6646
|
"mentions",
|
|
6650
6647
|
"-n",
|
|
@@ -6652,27 +6649,25 @@ function listMentionsViaBirdEffect({
|
|
|
6652
6649
|
]);
|
|
6653
6650
|
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6654
6651
|
return yield* normalizeBirdTweetsPayloadEffect(payload, "mentions");
|
|
6655
|
-
}
|
|
6656
|
-
|
|
6657
|
-
|
|
6652
|
+
}
|
|
6653
|
+
);
|
|
6654
|
+
var listTweetsViaBirdCommandEffect = Effect8.fn("bird.listTweets")(function* ({
|
|
6658
6655
|
command,
|
|
6659
6656
|
maxResults,
|
|
6660
6657
|
all,
|
|
6661
6658
|
maxPages
|
|
6662
6659
|
}) {
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
args.push("--max-pages", String(maxPages));
|
|
6669
|
-
}
|
|
6660
|
+
const args = [command, "-n", String(maxResults)];
|
|
6661
|
+
if (all) {
|
|
6662
|
+
args.push("--all");
|
|
6663
|
+
if (maxPages !== void 0) {
|
|
6664
|
+
args.push("--max-pages", String(maxPages));
|
|
6670
6665
|
}
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
}
|
|
6666
|
+
}
|
|
6667
|
+
const stdout = yield* runBirdTweetJsonCommandEffect(args);
|
|
6668
|
+
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6669
|
+
return yield* normalizeBirdTweetsPayloadEffect(payload, command);
|
|
6670
|
+
});
|
|
6676
6671
|
function listLikedTweetsViaBirdEffect(options) {
|
|
6677
6672
|
return listTweetsViaBirdCommandEffect({
|
|
6678
6673
|
command: "likes",
|
|
@@ -6685,8 +6680,8 @@ function listBookmarkedTweetsViaBirdEffect(options) {
|
|
|
6685
6680
|
...options
|
|
6686
6681
|
});
|
|
6687
6682
|
}
|
|
6688
|
-
|
|
6689
|
-
|
|
6683
|
+
var searchTweetsViaBirdEffect = Effect8.fn("bird.searchTweets")(
|
|
6684
|
+
function* (query, options) {
|
|
6690
6685
|
const args = ["search", query, "-n", String(options.maxResults)];
|
|
6691
6686
|
if (options.all) {
|
|
6692
6687
|
args.push("--all");
|
|
@@ -6697,30 +6692,30 @@ function searchTweetsViaBirdEffect(query, options) {
|
|
|
6697
6692
|
const stdout = yield* runBirdTweetJsonCommandEffect(args);
|
|
6698
6693
|
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6699
6694
|
return yield* normalizeBirdTweetsPayloadEffect(payload, "search");
|
|
6700
|
-
}
|
|
6701
|
-
|
|
6702
|
-
|
|
6695
|
+
}
|
|
6696
|
+
);
|
|
6697
|
+
var lookupTweetsByIdsViaBirdEffect = Effect8.fn(
|
|
6698
|
+
"bird.lookupTweetsByIds"
|
|
6699
|
+
)(function* (ids) {
|
|
6703
6700
|
if (ids.length === 0) {
|
|
6704
|
-
return
|
|
6705
|
-
}
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
}) {
|
|
6723
|
-
return Effect8.gen(function* () {
|
|
6701
|
+
return { data: [] };
|
|
6702
|
+
}
|
|
6703
|
+
const tweets = yield* Effect8.forEach(
|
|
6704
|
+
ids,
|
|
6705
|
+
Effect8.fn("bird.readTweet")(function* (id) {
|
|
6706
|
+
const stdout = yield* runBirdTweetJsonCommandEffect(["read", id]);
|
|
6707
|
+
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6708
|
+
return yield* normalizeBirdTweetItemEffect(payload, "read");
|
|
6709
|
+
}),
|
|
6710
|
+
{ concurrency: "unbounded" }
|
|
6711
|
+
);
|
|
6712
|
+
return normalizeBirdTweets(tweets);
|
|
6713
|
+
});
|
|
6714
|
+
var listHomeTimelineViaBirdEffect = Effect8.fn("bird.listHomeTimeline")(
|
|
6715
|
+
function* ({
|
|
6716
|
+
maxResults,
|
|
6717
|
+
following = true
|
|
6718
|
+
}) {
|
|
6724
6719
|
const args = ["home", "-n", String(maxResults)];
|
|
6725
6720
|
if (following) {
|
|
6726
6721
|
args.push("--following");
|
|
@@ -6728,8 +6723,8 @@ function listHomeTimelineViaBirdEffect({
|
|
|
6728
6723
|
const stdout = yield* runBirdTweetJsonCommandEffect(args);
|
|
6729
6724
|
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6730
6725
|
return yield* normalizeBirdTweetsPayloadEffect(payload, "home");
|
|
6731
|
-
}
|
|
6732
|
-
|
|
6726
|
+
}
|
|
6727
|
+
);
|
|
6733
6728
|
function normalizeBirdFollowUsers(payload, command, maxResults) {
|
|
6734
6729
|
const rawPayload = payload;
|
|
6735
6730
|
const users = Array.isArray(rawPayload) ? rawPayload : rawPayload.users;
|
|
@@ -6755,14 +6750,14 @@ function normalizeBirdFollowUsersEffect(payload, command, maxResults) {
|
|
|
6755
6750
|
catch: (error) => error
|
|
6756
6751
|
});
|
|
6757
6752
|
}
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6753
|
+
var listFollowUsersViaBirdEffect = Effect8.fn("bird.listFollowUsers")(
|
|
6754
|
+
function* ({
|
|
6755
|
+
direction,
|
|
6756
|
+
userId,
|
|
6757
|
+
maxResults,
|
|
6758
|
+
all,
|
|
6759
|
+
maxPages
|
|
6760
|
+
}) {
|
|
6766
6761
|
const args = [direction, "-n", String(maxResults), "--json"];
|
|
6767
6762
|
if (userId) {
|
|
6768
6763
|
args.push("--user", userId);
|
|
@@ -6780,8 +6775,8 @@ function listFollowUsersViaBirdEffect({
|
|
|
6780
6775
|
direction,
|
|
6781
6776
|
maxResults
|
|
6782
6777
|
);
|
|
6783
|
-
}
|
|
6784
|
-
|
|
6778
|
+
}
|
|
6779
|
+
);
|
|
6785
6780
|
function normalizeBirdLists(payload) {
|
|
6786
6781
|
if (!Array.isArray(payload)) {
|
|
6787
6782
|
throw new Error("bird lists returned unexpected JSON");
|
|
@@ -6805,10 +6800,8 @@ function normalizeBirdLists(payload) {
|
|
|
6805
6800
|
}).filter((item) => Boolean(item));
|
|
6806
6801
|
return { data, meta: { result_count: data.length, next_token: null } };
|
|
6807
6802
|
}
|
|
6808
|
-
|
|
6809
|
-
maxResults
|
|
6810
|
-
}) {
|
|
6811
|
-
return Effect8.gen(function* () {
|
|
6803
|
+
var listOwnedXListsViaBirdEffect = Effect8.fn("bird.listOwnedXLists")(
|
|
6804
|
+
function* ({ maxResults }) {
|
|
6812
6805
|
const stdout = yield* runBirdJsonCommandEffect([
|
|
6813
6806
|
"lists",
|
|
6814
6807
|
"-n",
|
|
@@ -6820,14 +6813,14 @@ function listOwnedXListsViaBirdEffect({
|
|
|
6820
6813
|
try: () => normalizeBirdLists(payload),
|
|
6821
6814
|
catch: (error) => error
|
|
6822
6815
|
});
|
|
6823
|
-
}
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6816
|
+
}
|
|
6817
|
+
);
|
|
6818
|
+
var listXListMembersViaBirdEffect = Effect8.fn("bird.listXListMembers")(
|
|
6819
|
+
function* ({
|
|
6820
|
+
listId,
|
|
6821
|
+
maxResults,
|
|
6822
|
+
maxPages = 1
|
|
6823
|
+
}) {
|
|
6831
6824
|
const args = ["list-members", listId, "-n", String(maxResults), "--json"];
|
|
6832
6825
|
if (maxPages > 1) {
|
|
6833
6826
|
args.push("--all", "--max-pages", String(maxPages));
|
|
@@ -6847,27 +6840,25 @@ function listXListMembersViaBirdEffect({
|
|
|
6847
6840
|
};
|
|
6848
6841
|
}
|
|
6849
6842
|
return normalized;
|
|
6850
|
-
}
|
|
6851
|
-
|
|
6852
|
-
|
|
6843
|
+
}
|
|
6844
|
+
);
|
|
6845
|
+
var listThreadViaBirdEffect = Effect8.fn("bird.listThread")(function* ({
|
|
6853
6846
|
tweetId,
|
|
6854
6847
|
all,
|
|
6855
6848
|
maxPages,
|
|
6856
6849
|
timeoutMs
|
|
6857
6850
|
}) {
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
});
|
|
6870
|
-
}
|
|
6851
|
+
const args = ["thread", tweetId];
|
|
6852
|
+
if (all) {
|
|
6853
|
+
args.push("--all");
|
|
6854
|
+
}
|
|
6855
|
+
if (maxPages !== void 0) {
|
|
6856
|
+
args.push("--max-pages", String(maxPages));
|
|
6857
|
+
}
|
|
6858
|
+
const stdout = yield* runBirdTweetJsonCommandEffect(args, timeoutMs);
|
|
6859
|
+
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6860
|
+
return yield* normalizeBirdTweetsPayloadEffect(payload, "thread");
|
|
6861
|
+
});
|
|
6871
6862
|
function normalizeBirdDmsPayloadEffect(payload) {
|
|
6872
6863
|
return Effect8.try({
|
|
6873
6864
|
try: () => {
|
|
@@ -6903,74 +6894,71 @@ function parseBirdWhoami(stdout) {
|
|
|
6903
6894
|
...name ? { name } : {}
|
|
6904
6895
|
};
|
|
6905
6896
|
}
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6897
|
+
var getAuthenticatedBirdAccountEffect = Effect8.fn(
|
|
6898
|
+
"bird.getAuthenticatedAccount"
|
|
6899
|
+
)(function* () {
|
|
6900
|
+
const stdout = yield* runBirdJsonCommandEffect(["whoami"]);
|
|
6901
|
+
return yield* Effect8.try({
|
|
6902
|
+
try: () => parseBirdWhoami(stdout),
|
|
6903
|
+
catch: (error) => error
|
|
6913
6904
|
});
|
|
6914
|
-
}
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
function listDirectMessagesViaBirdEffect({
|
|
6905
|
+
});
|
|
6906
|
+
var listDirectMessagesViaBirdEffect = Effect8.fn(
|
|
6907
|
+
"bird.listDirectMessages"
|
|
6908
|
+
)(function* ({
|
|
6919
6909
|
maxResults,
|
|
6920
6910
|
inbox = "all",
|
|
6921
6911
|
maxPages,
|
|
6922
6912
|
allPages = false,
|
|
6923
6913
|
pageDelayMs
|
|
6924
6914
|
}) {
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
function
|
|
6915
|
+
const args = ["dms", "-n", String(maxResults), "--json"];
|
|
6916
|
+
if (inbox !== "all") {
|
|
6917
|
+
args.push("--inbox", inbox);
|
|
6918
|
+
}
|
|
6919
|
+
if (allPages) {
|
|
6920
|
+
args.push("--all-pages");
|
|
6921
|
+
} else if (typeof maxPages === "number") {
|
|
6922
|
+
args.push("--max-pages", String(maxPages));
|
|
6923
|
+
}
|
|
6924
|
+
if (typeof pageDelayMs === "number" && pageDelayMs > 0) {
|
|
6925
|
+
args.push("--page-delay-ms", String(pageDelayMs));
|
|
6926
|
+
}
|
|
6927
|
+
const stdout = yield* runBirdJsonCommandEffect(args);
|
|
6928
|
+
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6929
|
+
return yield* normalizeBirdDmsPayloadEffect(payload);
|
|
6930
|
+
});
|
|
6931
|
+
var runDirectMessageRequestMutationViaBirdEffect = Effect8.fn(
|
|
6932
|
+
"bird.runDirectMessageRequestMutation"
|
|
6933
|
+
)(function* ({
|
|
6944
6934
|
action,
|
|
6945
6935
|
conversationId,
|
|
6946
6936
|
maxPages,
|
|
6947
6937
|
allPages = false
|
|
6948
6938
|
}) {
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
if (
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
args.push("--max-pages", String(maxPages));
|
|
6957
|
-
}
|
|
6958
|
-
}
|
|
6959
|
-
const stdout = yield* runBirdJsonCommandAllowFailureEffect(args);
|
|
6960
|
-
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6961
|
-
if (payload && typeof payload === "object" && typeof payload.success === "boolean") {
|
|
6962
|
-
return payload;
|
|
6939
|
+
const command = action === "accept" ? "dm-accept" : action === "reject" ? "dm-reject" : "dm-block";
|
|
6940
|
+
const args = [command, conversationId, "--json"];
|
|
6941
|
+
if (action === "block") {
|
|
6942
|
+
if (allPages) {
|
|
6943
|
+
args.push("--all-pages");
|
|
6944
|
+
} else if (typeof maxPages === "number") {
|
|
6945
|
+
args.push("--max-pages", String(maxPages));
|
|
6963
6946
|
}
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6947
|
+
}
|
|
6948
|
+
const stdout = yield* runBirdJsonCommandAllowFailureEffect(args);
|
|
6949
|
+
const payload = yield* parseBirdJsonEffect(stdout);
|
|
6950
|
+
if (payload && typeof payload === "object" && typeof payload.success === "boolean") {
|
|
6951
|
+
return payload;
|
|
6952
|
+
}
|
|
6953
|
+
throw new Error(`bird ${command} returned unexpected JSON`);
|
|
6954
|
+
});
|
|
6967
6955
|
function runDirectMessageRequestMutationViaBird(options) {
|
|
6968
6956
|
return runEffectPromise(
|
|
6969
6957
|
runDirectMessageRequestMutationViaBirdEffect(options)
|
|
6970
6958
|
);
|
|
6971
6959
|
}
|
|
6972
|
-
|
|
6973
|
-
|
|
6960
|
+
var lookupProfileViaBirdEffect = Effect8.fn("bird.lookupProfile")(
|
|
6961
|
+
function* (usernameOrId) {
|
|
6974
6962
|
const target = usernameOrId.trim().replace(/^@/, "");
|
|
6975
6963
|
if (!target) {
|
|
6976
6964
|
return null;
|
|
@@ -6998,8 +6986,8 @@ function lookupProfileViaBirdEffect(usernameOrId) {
|
|
|
6998
6986
|
stdout
|
|
6999
6987
|
);
|
|
7000
6988
|
return toXurlMentionUser(payload.user);
|
|
7001
|
-
}
|
|
7002
|
-
|
|
6989
|
+
}
|
|
6990
|
+
);
|
|
7003
6991
|
function toXurlMentionUser(user) {
|
|
7004
6992
|
if (!user?.id || !user.username) {
|
|
7005
6993
|
return null;
|
|
@@ -7023,7 +7011,7 @@ function toXurlMentionUser(user) {
|
|
|
7023
7011
|
}
|
|
7024
7012
|
};
|
|
7025
7013
|
}
|
|
7026
|
-
|
|
7014
|
+
var lookupProfilesViaBirdEffect = Effect8.fn("bird.lookupProfiles")((usernameOrIds) => {
|
|
7027
7015
|
const targets = Array.from(
|
|
7028
7016
|
new Set(
|
|
7029
7017
|
usernameOrIds.map((target) => target.trim().replace(/^@/, "")).filter((target) => target.length > 0)
|
|
@@ -7077,10 +7065,10 @@ function lookupProfilesViaBirdEffect(usernameOrIds) {
|
|
|
7077
7065
|
);
|
|
7078
7066
|
})
|
|
7079
7067
|
);
|
|
7080
|
-
}
|
|
7068
|
+
});
|
|
7081
7069
|
|
|
7082
7070
|
// src/lib/xurl.ts
|
|
7083
|
-
import { Effect as Effect9 } from "effect";
|
|
7071
|
+
import { Data as Data3, Duration, Effect as Effect9, Schedule } from "effect";
|
|
7084
7072
|
var TRANSPORT_STATUS_TTL_MS = 5 * 6e4;
|
|
7085
7073
|
var AUTHENTICATED_USER_TTL_MS = 6e4;
|
|
7086
7074
|
var JSON_RETRY_LIMIT = 6;
|
|
@@ -7091,8 +7079,18 @@ var RICH_USER_FIELDS = "description,entities,location,public_metrics,profile_ima
|
|
|
7091
7079
|
var DM_EVENT_FIELDS = "attachments,created_at,dm_conversation_id,entities,event_type,id,participant_ids,referenced_tweets,sender_id,text";
|
|
7092
7080
|
var THREAD_TWEET_FIELDS = "created_at,conversation_id,entities,public_metrics,referenced_tweets,in_reply_to_user_id,attachments";
|
|
7093
7081
|
var BOOKMARKS_MAX_RESULTS_CAP = 90;
|
|
7094
|
-
var
|
|
7095
|
-
|
|
7082
|
+
var cachedTransportStatus = Effect9.runSync(
|
|
7083
|
+
Effect9.cachedWithTTL(
|
|
7084
|
+
Effect9.suspend(() => readTransportStatusEffect()),
|
|
7085
|
+
Duration.millis(TRANSPORT_STATUS_TTL_MS)
|
|
7086
|
+
)
|
|
7087
|
+
);
|
|
7088
|
+
var [cachedAuthenticatedUser, invalidateAuthenticatedUser] = Effect9.runSync(
|
|
7089
|
+
Effect9.cachedInvalidateWithTTL(
|
|
7090
|
+
Effect9.suspend(() => lookupAuthenticatedUserFreshEffect()),
|
|
7091
|
+
Duration.millis(AUTHENTICATED_USER_TTL_MS)
|
|
7092
|
+
)
|
|
7093
|
+
);
|
|
7096
7094
|
var oauth2CandidateCache = /* @__PURE__ */ new Map();
|
|
7097
7095
|
function liveWritesDisabled() {
|
|
7098
7096
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
@@ -7120,8 +7118,16 @@ function formatExecError(error, fallback) {
|
|
|
7120
7118
|
}
|
|
7121
7119
|
return parts.join("\n");
|
|
7122
7120
|
}
|
|
7123
|
-
|
|
7124
|
-
|
|
7121
|
+
var XurlCommandError = class extends Data3.TaggedError("XurlCommandError") {
|
|
7122
|
+
};
|
|
7123
|
+
function toXurlCommandError(cause, args) {
|
|
7124
|
+
if (cause instanceof XurlCommandError) return cause;
|
|
7125
|
+
return new XurlCommandError({
|
|
7126
|
+
message: formatExecError(cause, `xurl ${args.join(" ")} failed`),
|
|
7127
|
+
commandArgs: args,
|
|
7128
|
+
rateLimited: isRateLimitedFailure(cause),
|
|
7129
|
+
cause
|
|
7130
|
+
});
|
|
7125
7131
|
}
|
|
7126
7132
|
function normalizeError(error) {
|
|
7127
7133
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -7139,14 +7145,11 @@ function parseErrorPayload(error) {
|
|
|
7139
7145
|
return null;
|
|
7140
7146
|
}
|
|
7141
7147
|
}
|
|
7142
|
-
function
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
}
|
|
7148
|
-
const baseDelay = getJsonRetryBaseDelayMs();
|
|
7149
|
-
return Math.min(baseDelay * 2 ** attempt, 3e4);
|
|
7148
|
+
function isRateLimitedFailure(error) {
|
|
7149
|
+
return Number(parseErrorPayload(error)?.status ?? 0) === 429;
|
|
7150
|
+
}
|
|
7151
|
+
function jsonRetryDelayMs(retries) {
|
|
7152
|
+
return Math.min(getJsonRetryBaseDelayMs() * 2 ** retries, 3e4);
|
|
7150
7153
|
}
|
|
7151
7154
|
function emitJsonCommandAttempt(options, attempt) {
|
|
7152
7155
|
try {
|
|
@@ -7168,8 +7171,8 @@ function isUnauthenticatedXurlStatus(status) {
|
|
|
7168
7171
|
status
|
|
7169
7172
|
);
|
|
7170
7173
|
}
|
|
7171
|
-
|
|
7172
|
-
|
|
7174
|
+
var readTransportStatusEffect = Effect9.fn("xurl.readTransportStatus")(
|
|
7175
|
+
function* () {
|
|
7173
7176
|
const installed = yield* hasXurlEffect();
|
|
7174
7177
|
if (!installed) {
|
|
7175
7178
|
return {
|
|
@@ -7207,68 +7210,31 @@ function readTransportStatusEffect() {
|
|
|
7207
7210
|
})
|
|
7208
7211
|
)
|
|
7209
7212
|
);
|
|
7210
|
-
}
|
|
7211
|
-
|
|
7213
|
+
}
|
|
7214
|
+
);
|
|
7212
7215
|
function getTransportStatusEffect() {
|
|
7213
|
-
return
|
|
7214
|
-
const now2 = Date.now();
|
|
7215
|
-
if (transportStatusCache?.value && transportStatusCache.expiresAt > now2) {
|
|
7216
|
-
return transportStatusCache.value;
|
|
7217
|
-
}
|
|
7218
|
-
if (transportStatusCache?.pending) {
|
|
7219
|
-
const status2 = yield* Effect9.tryPromise({
|
|
7220
|
-
try: () => transportStatusCache?.pending ?? Promise.resolve(void 0),
|
|
7221
|
-
catch: normalizeError
|
|
7222
|
-
});
|
|
7223
|
-
if (status2) return status2;
|
|
7224
|
-
}
|
|
7225
|
-
const pending = runEffectPromise(readTransportStatusEffect());
|
|
7226
|
-
transportStatusCache = {
|
|
7227
|
-
expiresAt: 0,
|
|
7228
|
-
pending
|
|
7229
|
-
};
|
|
7230
|
-
const status = yield* Effect9.tryPromise({
|
|
7231
|
-
try: () => pending,
|
|
7232
|
-
catch: normalizeError
|
|
7233
|
-
}).pipe(
|
|
7234
|
-
Effect9.catchAll(
|
|
7235
|
-
(error) => Effect9.sync(() => {
|
|
7236
|
-
transportStatusCache = void 0;
|
|
7237
|
-
}).pipe(Effect9.flatMap(() => Effect9.fail(error)))
|
|
7238
|
-
)
|
|
7239
|
-
);
|
|
7240
|
-
transportStatusCache = {
|
|
7241
|
-
expiresAt: Date.now() + TRANSPORT_STATUS_TTL_MS,
|
|
7242
|
-
value: status
|
|
7243
|
-
};
|
|
7244
|
-
return status;
|
|
7245
|
-
});
|
|
7216
|
+
return cachedTransportStatus;
|
|
7246
7217
|
}
|
|
7247
|
-
function
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
return Effect9.gen(function* () {
|
|
7252
|
-
if (liveWritesDisabled()) {
|
|
7253
|
-
if (e2eFakeLiveWritesEnabled()) {
|
|
7254
|
-
return { ok: true, output: "e2e fake live write" };
|
|
7255
|
-
}
|
|
7256
|
-
return { ok: false, output: "live writes disabled" };
|
|
7218
|
+
var runShortcutEffect = Effect9.fn("xurl.runShortcut")(function* (args) {
|
|
7219
|
+
if (liveWritesDisabled()) {
|
|
7220
|
+
if (e2eFakeLiveWritesEnabled()) {
|
|
7221
|
+
return { ok: true, output: "e2e fake live write" };
|
|
7257
7222
|
}
|
|
7258
|
-
return
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7223
|
+
return { ok: false, output: "live writes disabled" };
|
|
7224
|
+
}
|
|
7225
|
+
return yield* runSubprocessEffect({ command: "xurl", args }).pipe(
|
|
7226
|
+
Effect9.map(({ stdout, stderr }) => ({
|
|
7227
|
+
ok: true,
|
|
7228
|
+
output: stdout || stderr
|
|
7229
|
+
})),
|
|
7230
|
+
Effect9.catchAll(
|
|
7231
|
+
(error) => Effect9.succeed({
|
|
7232
|
+
ok: false,
|
|
7233
|
+
output: formatExecError(error, "xurl execution failed")
|
|
7234
|
+
})
|
|
7235
|
+
)
|
|
7236
|
+
);
|
|
7237
|
+
});
|
|
7272
7238
|
function execXurlJsonEffect(args, timeoutMs, signal) {
|
|
7273
7239
|
return runSubprocessEffect({
|
|
7274
7240
|
command: "xurl",
|
|
@@ -7301,47 +7267,50 @@ function execXurlTextEffect(args, deadlineMs) {
|
|
|
7301
7267
|
function parseJsonPayloadEffect(stdout, args) {
|
|
7302
7268
|
return Effect9.try({
|
|
7303
7269
|
try: () => JSON.parse(stdout),
|
|
7304
|
-
catch: (error) =>
|
|
7305
|
-
});
|
|
7306
|
-
}
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7270
|
+
catch: (error) => toXurlCommandError(error, args)
|
|
7271
|
+
});
|
|
7272
|
+
}
|
|
7273
|
+
var runJsonCommandEffect = Effect9.fn("xurl.runJsonCommand")(function* (args, options = {}) {
|
|
7274
|
+
const deadlineMs = options.deadlineMs ?? (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
|
|
7275
|
+
let attempt = 0;
|
|
7276
|
+
const attemptCommand = Effect9.suspend(
|
|
7277
|
+
() => execXurlJsonEffect(
|
|
7278
|
+
args,
|
|
7279
|
+
deadlineMs === void 0 ? void 0 : Math.max(0, deadlineMs - Date.now()),
|
|
7280
|
+
options.signal
|
|
7281
|
+
)
|
|
7282
|
+
).pipe(
|
|
7283
|
+
Effect9.flatMap(({ stdout }) => parseJsonPayloadEffect(stdout, args)),
|
|
7284
|
+
Effect9.mapError((error) => toXurlCommandError(error, args)),
|
|
7285
|
+
Effect9.tap(
|
|
7286
|
+
() => Effect9.sync(
|
|
7287
|
+
() => emitJsonCommandAttempt(options, { args, attempt, status: "ok" })
|
|
7288
|
+
)
|
|
7289
|
+
),
|
|
7290
|
+
Effect9.tapError(
|
|
7291
|
+
(error) => Effect9.sync(() => {
|
|
7320
7292
|
emitJsonCommandAttempt(options, {
|
|
7321
7293
|
args,
|
|
7322
7294
|
attempt,
|
|
7323
|
-
status:
|
|
7295
|
+
status: error.rateLimited ? "rate_limited" : "error",
|
|
7324
7296
|
error
|
|
7325
7297
|
});
|
|
7326
|
-
|
|
7327
|
-
return Effect9.fail(formatXurlCommandError(error, args));
|
|
7328
|
-
}
|
|
7329
|
-
if (options.signal?.aborted) {
|
|
7330
|
-
return Effect9.fail(formatXurlCommandError(error, args));
|
|
7331
|
-
}
|
|
7332
|
-
const remainingMs = deadlineMs ? Math.max(0, deadlineMs - Date.now()) : void 0;
|
|
7333
|
-
if (remainingMs !== void 0 && retryDelayMs >= remainingMs) {
|
|
7334
|
-
return Effect9.fail(formatXurlCommandError(error, args));
|
|
7335
|
-
}
|
|
7336
|
-
return Effect9.sleep(retryDelayMs).pipe(
|
|
7337
|
-
Effect9.flatMap(
|
|
7338
|
-
() => runJsonCommandEffect(args, { ...options, deadlineMs }, attempt + 1)
|
|
7339
|
-
)
|
|
7340
|
-
);
|
|
7298
|
+
attempt += 1;
|
|
7341
7299
|
})
|
|
7342
|
-
)
|
|
7343
|
-
|
|
7344
|
-
|
|
7300
|
+
)
|
|
7301
|
+
);
|
|
7302
|
+
const rateLimitRetries = Schedule.recurs(JSON_RETRY_LIMIT - 1).pipe(
|
|
7303
|
+
Schedule.whileInput(
|
|
7304
|
+
(error) => error.rateLimited && options.signal?.aborted !== true
|
|
7305
|
+
),
|
|
7306
|
+
Schedule.check((_error, retries) => {
|
|
7307
|
+
if (deadlineMs === void 0) return true;
|
|
7308
|
+
return jsonRetryDelayMs(retries) < Math.max(0, deadlineMs - Date.now());
|
|
7309
|
+
}),
|
|
7310
|
+
Schedule.addDelay((retries) => Duration.millis(jsonRetryDelayMs(retries)))
|
|
7311
|
+
);
|
|
7312
|
+
return yield* Effect9.retry(attemptCommand, rateLimitRetries);
|
|
7313
|
+
});
|
|
7345
7314
|
function cleanXurlUsernameLabel(username) {
|
|
7346
7315
|
const label = username?.trim().replace(/^@/, "");
|
|
7347
7316
|
return label && label !== "-" && label !== "\u2013" && label !== "(none)" && label.toLowerCase() !== "none" && label.toLowerCase() !== "unknown" && /^[^\s]{1,128}$/.test(label) ? label : void 0;
|
|
@@ -7375,33 +7344,35 @@ function parseOAuth2UsernamesFromStatus(rawStatus) {
|
|
|
7375
7344
|
}
|
|
7376
7345
|
return usernames;
|
|
7377
7346
|
}
|
|
7378
|
-
|
|
7379
|
-
|
|
7347
|
+
var readOAuth2UsernameCandidatesEffect = Effect9.fn(
|
|
7348
|
+
"xurl.readOAuth2UsernameCandidates"
|
|
7349
|
+
)(
|
|
7350
|
+
(deadlineMs) => execXurlTextEffect(["auth", "status"], deadlineMs).pipe(
|
|
7380
7351
|
Effect9.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)),
|
|
7381
7352
|
Effect9.catchAll(() => Effect9.succeed([]))
|
|
7382
|
-
)
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7353
|
+
)
|
|
7354
|
+
);
|
|
7355
|
+
var lookupOAuth2UsernameForAccountEffect = Effect9.fn(
|
|
7356
|
+
"xurl.lookupOAuth2UsernameForAccount"
|
|
7357
|
+
)(function* (expectedUsername, attemptedUsernames, deadlineMs, knownCandidates) {
|
|
7358
|
+
const expected = comparableXurlUsername(expectedUsername);
|
|
7359
|
+
if (!expected) return void 0;
|
|
7360
|
+
const candidates = knownCandidates ?? (yield* readOAuth2UsernameCandidatesEffect(deadlineMs));
|
|
7361
|
+
for (const candidate of candidates) {
|
|
7362
|
+
const candidateKey = `${candidate.app ?? "default"}:${candidate.username}`;
|
|
7363
|
+
if (attemptedUsernames.has(candidateKey)) continue;
|
|
7364
|
+
const payload = yield* runJsonCommandEffect(
|
|
7365
|
+
oauth2ArgsForCandidate(candidate, ["/2/users/me"]),
|
|
7366
|
+
{ deadlineMs }
|
|
7367
|
+
).pipe(Effect9.catchAll(() => Effect9.succeed(null)));
|
|
7368
|
+
const user = payload ? authenticatedUserFromPayload(payload) : null;
|
|
7369
|
+
const actual = comparableXurlUsername(String(user?.username ?? ""));
|
|
7370
|
+
if (actual === expected) {
|
|
7371
|
+
return candidate;
|
|
7401
7372
|
}
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
}
|
|
7373
|
+
}
|
|
7374
|
+
return void 0;
|
|
7375
|
+
});
|
|
7405
7376
|
function oauth2ArgsForCandidate(candidate, args) {
|
|
7406
7377
|
return [
|
|
7407
7378
|
...candidate?.app ? ["--app", candidate.app] : [],
|
|
@@ -7424,14 +7395,14 @@ function configuredOAuth2Candidate(primaryUsername) {
|
|
|
7424
7395
|
...effectiveUsername ? { username: effectiveUsername } : {}
|
|
7425
7396
|
};
|
|
7426
7397
|
}
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7398
|
+
var runOAuth2JsonCommandEffect = Effect9.fn("xurl.runOAuth2JsonCommand")(
|
|
7399
|
+
function* ({
|
|
7400
|
+
args,
|
|
7401
|
+
username,
|
|
7402
|
+
options,
|
|
7403
|
+
useConfiguredCandidate = true
|
|
7404
|
+
}) {
|
|
7405
|
+
const primaryUsername = cleanXurlUsernameLabel(username);
|
|
7435
7406
|
const deadlineMs = options?.deadlineMs ?? (typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
|
|
7436
7407
|
const scopedOptions = { ...options, deadlineMs };
|
|
7437
7408
|
let authCandidate = primaryUsername ? { username: primaryUsername } : void 0;
|
|
@@ -7478,7 +7449,12 @@ function runOAuth2JsonCommandEffect({
|
|
|
7478
7449
|
}
|
|
7479
7450
|
}
|
|
7480
7451
|
if (deadlineMs !== void 0 && Date.now() >= deadlineMs) {
|
|
7481
|
-
return yield*
|
|
7452
|
+
return yield* new XurlCommandError({
|
|
7453
|
+
message: "xurl OAuth2 fallback timed out",
|
|
7454
|
+
commandArgs: args,
|
|
7455
|
+
rateLimited: false,
|
|
7456
|
+
cause: void 0
|
|
7457
|
+
});
|
|
7482
7458
|
}
|
|
7483
7459
|
return yield* runJsonCommandEffect(
|
|
7484
7460
|
oauth2ArgsForCandidate(authCandidate, args),
|
|
@@ -7517,10 +7493,10 @@ function runOAuth2JsonCommandEffect({
|
|
|
7517
7493
|
);
|
|
7518
7494
|
})
|
|
7519
7495
|
);
|
|
7520
|
-
}
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7496
|
+
}
|
|
7497
|
+
);
|
|
7498
|
+
var runMutationCommandEffect = Effect9.fn("xurl.runMutationCommand")(
|
|
7499
|
+
function* (args) {
|
|
7524
7500
|
if (liveWritesDisabled()) {
|
|
7525
7501
|
if (e2eFakeLiveWritesEnabled()) {
|
|
7526
7502
|
return { ok: true, output: "e2e fake live write" };
|
|
@@ -7539,9 +7515,9 @@ function runMutationCommandEffect(args) {
|
|
|
7539
7515
|
})
|
|
7540
7516
|
)
|
|
7541
7517
|
);
|
|
7542
|
-
}
|
|
7543
|
-
|
|
7544
|
-
|
|
7518
|
+
}
|
|
7519
|
+
);
|
|
7520
|
+
var lookupUsersByIdsEffect = Effect9.fn("xurl.lookupUsersByIds")((ids) => {
|
|
7545
7521
|
if (ids.length === 0) {
|
|
7546
7522
|
return Effect9.succeed([]);
|
|
7547
7523
|
}
|
|
@@ -7554,10 +7530,7 @@ function lookupUsersByIdsEffect(ids) {
|
|
|
7554
7530
|
(payload) => Array.isArray(payload.data) ? payload.data : []
|
|
7555
7531
|
)
|
|
7556
7532
|
);
|
|
7557
|
-
}
|
|
7558
|
-
function lookupUsersByIds(ids) {
|
|
7559
|
-
return runEffectPromise(lookupUsersByIdsEffect(ids));
|
|
7560
|
-
}
|
|
7533
|
+
});
|
|
7561
7534
|
function normalizeXListPage(payload) {
|
|
7562
7535
|
const rawItems = Array.isArray(payload.data) ? payload.data : [];
|
|
7563
7536
|
const data = rawItems.map((value) => {
|
|
@@ -7586,12 +7559,12 @@ function normalizeXListPage(payload) {
|
|
|
7586
7559
|
}
|
|
7587
7560
|
};
|
|
7588
7561
|
}
|
|
7589
|
-
|
|
7562
|
+
var listOwnedXListsViaXurlEffect = Effect9.fn("xurl.listOwnedXLists")(({
|
|
7590
7563
|
userId,
|
|
7591
7564
|
username,
|
|
7592
7565
|
maxResults,
|
|
7593
7566
|
paginationToken
|
|
7594
|
-
}) {
|
|
7567
|
+
}) => {
|
|
7595
7568
|
const query = new URLSearchParams({
|
|
7596
7569
|
max_results: String(maxResults),
|
|
7597
7570
|
"list.fields": "created_at,description,follower_count,member_count,name,owner_id,private"
|
|
@@ -7601,29 +7574,33 @@ function listOwnedXListsViaXurlEffect({
|
|
|
7601
7574
|
args: [`/2/users/${userId}/owned_lists?${query.toString()}`],
|
|
7602
7575
|
username
|
|
7603
7576
|
}).pipe(Effect9.map(normalizeXListPage));
|
|
7604
|
-
}
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7626
|
-
|
|
7577
|
+
});
|
|
7578
|
+
var listXListMembersViaXurlEffect = Effect9.fn("xurl.listXListMembers")(
|
|
7579
|
+
({
|
|
7580
|
+
listId,
|
|
7581
|
+
username,
|
|
7582
|
+
maxResults,
|
|
7583
|
+
paginationToken
|
|
7584
|
+
}) => {
|
|
7585
|
+
const query = new URLSearchParams({
|
|
7586
|
+
max_results: String(maxResults),
|
|
7587
|
+
"user.fields": RICH_USER_FIELDS
|
|
7588
|
+
});
|
|
7589
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
7590
|
+
return runOAuth2JsonCommandEffect({
|
|
7591
|
+
args: [`/2/lists/${listId}/members?${query.toString()}`],
|
|
7592
|
+
username
|
|
7593
|
+
}).pipe(
|
|
7594
|
+
Effect9.map((payload) => ({
|
|
7595
|
+
data: Array.isArray(payload.data) ? payload.data : [],
|
|
7596
|
+
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : {}
|
|
7597
|
+
}))
|
|
7598
|
+
);
|
|
7599
|
+
}
|
|
7600
|
+
);
|
|
7601
|
+
var lookupUsersByHandlesEffect = Effect9.fn(
|
|
7602
|
+
"xurl.lookupUsersByHandles"
|
|
7603
|
+
)((handles, options = {}) => {
|
|
7627
7604
|
if (handles.length === 0) {
|
|
7628
7605
|
return Effect9.succeed([]);
|
|
7629
7606
|
}
|
|
@@ -7643,98 +7620,65 @@ function lookupUsersByHandlesEffect(handles, options = {}) {
|
|
|
7643
7620
|
(payload) => Array.isArray(payload.data) ? payload.data : []
|
|
7644
7621
|
)
|
|
7645
7622
|
);
|
|
7646
|
-
}
|
|
7647
|
-
function lookupUsersByHandles(handles, options = {}) {
|
|
7648
|
-
return runEffectPromise(lookupUsersByHandlesEffect(handles, options));
|
|
7649
|
-
}
|
|
7623
|
+
});
|
|
7650
7624
|
function authenticatedUserFromPayload(payload) {
|
|
7651
7625
|
const data = payload.data;
|
|
7652
7626
|
return data && typeof data === "object" ? data : null;
|
|
7653
7627
|
}
|
|
7654
|
-
|
|
7655
|
-
|
|
7628
|
+
var lookupAuthenticatedUserFreshEffect = Effect9.fn(
|
|
7629
|
+
"xurl.lookupAuthenticatedUserFresh"
|
|
7630
|
+
)(
|
|
7631
|
+
() => runJsonCommandEffect(["whoami"]).pipe(
|
|
7656
7632
|
Effect9.map(authenticatedUserFromPayload)
|
|
7657
|
-
)
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7633
|
+
)
|
|
7634
|
+
);
|
|
7635
|
+
var lookupAuthenticatedOAuth2UserEffect = Effect9.fn(
|
|
7636
|
+
"xurl.lookupAuthenticatedOAuth2User"
|
|
7637
|
+
)(
|
|
7638
|
+
(username) => runOAuth2JsonCommandEffect({
|
|
7661
7639
|
args: ["whoami"],
|
|
7662
7640
|
username
|
|
7663
|
-
}).pipe(Effect9.map(authenticatedUserFromPayload))
|
|
7664
|
-
|
|
7641
|
+
}).pipe(Effect9.map(authenticatedUserFromPayload))
|
|
7642
|
+
);
|
|
7665
7643
|
function lookupAuthenticatedUserEffect() {
|
|
7666
|
-
return
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
return authenticatedUserCache.value ?? null;
|
|
7670
|
-
}
|
|
7671
|
-
if (authenticatedUserCache?.pending) {
|
|
7672
|
-
return yield* Effect9.tryPromise({
|
|
7673
|
-
try: () => authenticatedUserCache?.pending ?? Promise.resolve(null),
|
|
7674
|
-
catch: normalizeError
|
|
7675
|
-
});
|
|
7676
|
-
}
|
|
7677
|
-
const pending = runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
7678
|
-
authenticatedUserCache = {
|
|
7679
|
-
expiresAt: 0,
|
|
7680
|
-
pending
|
|
7681
|
-
};
|
|
7682
|
-
const value = yield* Effect9.tryPromise({
|
|
7683
|
-
try: () => pending,
|
|
7684
|
-
catch: normalizeError
|
|
7685
|
-
}).pipe(
|
|
7686
|
-
Effect9.catchAll(
|
|
7687
|
-
(error) => Effect9.sync(() => {
|
|
7688
|
-
authenticatedUserCache = void 0;
|
|
7689
|
-
}).pipe(Effect9.flatMap(() => Effect9.fail(error)))
|
|
7690
|
-
)
|
|
7691
|
-
);
|
|
7692
|
-
authenticatedUserCache = {
|
|
7693
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
7694
|
-
value
|
|
7695
|
-
};
|
|
7696
|
-
return value;
|
|
7697
|
-
});
|
|
7698
|
-
}
|
|
7699
|
-
function lookupAuthenticatedUser() {
|
|
7700
|
-
return runEffectPromise(lookupAuthenticatedUserEffect());
|
|
7644
|
+
return cachedAuthenticatedUser.pipe(
|
|
7645
|
+
Effect9.tapError(() => invalidateAuthenticatedUser)
|
|
7646
|
+
);
|
|
7701
7647
|
}
|
|
7702
7648
|
function lookupAuthenticatedUserFresh() {
|
|
7703
7649
|
return runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
7704
7650
|
}
|
|
7705
|
-
|
|
7651
|
+
var resolveUserIdEffect = Effect9.fn("xurl.resolveUserId")(function* ({
|
|
7706
7652
|
username,
|
|
7707
7653
|
userId
|
|
7708
7654
|
}) {
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
if (!user2?.id) {
|
|
7714
|
-
return yield* Effect9.fail(
|
|
7715
|
-
new Error(`Could not resolve Twitter user id for @${username}`)
|
|
7716
|
-
);
|
|
7717
|
-
}
|
|
7718
|
-
return String(user2.id);
|
|
7719
|
-
}
|
|
7720
|
-
const user = yield* lookupAuthenticatedUserEffect();
|
|
7721
|
-
if (!user?.id) {
|
|
7655
|
+
if (userId) return userId;
|
|
7656
|
+
if (username) {
|
|
7657
|
+
const [user2] = yield* lookupUsersByHandlesEffect([username]);
|
|
7658
|
+
if (!user2?.id) {
|
|
7722
7659
|
return yield* Effect9.fail(
|
|
7723
|
-
new Error(
|
|
7660
|
+
new Error(`Could not resolve Twitter user id for @${username}`)
|
|
7724
7661
|
);
|
|
7725
7662
|
}
|
|
7726
|
-
return String(
|
|
7727
|
-
}
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7663
|
+
return String(user2.id);
|
|
7664
|
+
}
|
|
7665
|
+
const user = yield* lookupAuthenticatedUserEffect();
|
|
7666
|
+
if (!user?.id) {
|
|
7667
|
+
return yield* Effect9.fail(
|
|
7668
|
+
new Error("Could not resolve authenticated Twitter user id")
|
|
7669
|
+
);
|
|
7670
|
+
}
|
|
7671
|
+
return String(user.id);
|
|
7672
|
+
});
|
|
7673
|
+
var listMentionsViaXurlEffect = Effect9.fn("xurl.listMentions")(
|
|
7674
|
+
function* ({
|
|
7675
|
+
maxResults,
|
|
7676
|
+
username,
|
|
7677
|
+
userId,
|
|
7678
|
+
paginationToken,
|
|
7679
|
+
sinceId,
|
|
7680
|
+
startTime
|
|
7681
|
+
}) {
|
|
7738
7682
|
const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
|
|
7739
7683
|
const query = new URLSearchParams({
|
|
7740
7684
|
max_results: String(maxResults),
|
|
@@ -7757,19 +7701,16 @@ function listMentionsViaXurlEffect({
|
|
|
7757
7701
|
username
|
|
7758
7702
|
});
|
|
7759
7703
|
return toXurlMentionsResponse(payload);
|
|
7760
|
-
}
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
timeoutMs
|
|
7771
|
-
}) {
|
|
7772
|
-
return Effect9.gen(function* () {
|
|
7704
|
+
}
|
|
7705
|
+
);
|
|
7706
|
+
var listHomeTimelineViaXurlEffect = Effect9.fn("xurl.listHomeTimeline")(
|
|
7707
|
+
function* ({
|
|
7708
|
+
maxResults,
|
|
7709
|
+
username,
|
|
7710
|
+
userId,
|
|
7711
|
+
paginationToken,
|
|
7712
|
+
timeoutMs
|
|
7713
|
+
}) {
|
|
7773
7714
|
const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
|
|
7774
7715
|
const query = new URLSearchParams({
|
|
7775
7716
|
max_results: String(maxResults),
|
|
@@ -7789,8 +7730,8 @@ function listHomeTimelineViaXurlEffect({
|
|
|
7789
7730
|
options: { timeoutMs }
|
|
7790
7731
|
});
|
|
7791
7732
|
return toXurlMentionsResponse(payload);
|
|
7792
|
-
}
|
|
7793
|
-
|
|
7733
|
+
}
|
|
7734
|
+
);
|
|
7794
7735
|
function toXurlMentionsResponse(payload) {
|
|
7795
7736
|
return {
|
|
7796
7737
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
@@ -7798,7 +7739,9 @@ function toXurlMentionsResponse(payload) {
|
|
|
7798
7739
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
7799
7740
|
};
|
|
7800
7741
|
}
|
|
7801
|
-
|
|
7742
|
+
var listTimelineCollectionViaXurlEffect = Effect9.fn(
|
|
7743
|
+
"xurl.listTimelineCollection"
|
|
7744
|
+
)(function* ({
|
|
7802
7745
|
collection,
|
|
7803
7746
|
maxResults,
|
|
7804
7747
|
username,
|
|
@@ -7806,48 +7749,40 @@ function listTimelineCollectionViaXurlEffect({
|
|
|
7806
7749
|
isPaginatedWalk = false,
|
|
7807
7750
|
paginationToken
|
|
7808
7751
|
}) {
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
7822
|
-
});
|
|
7823
|
-
if (paginationToken) {
|
|
7824
|
-
query.set("pagination_token", paginationToken);
|
|
7825
|
-
}
|
|
7826
|
-
const payload = yield* runOAuth2JsonCommandEffect({
|
|
7827
|
-
args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
|
|
7828
|
-
username
|
|
7829
|
-
});
|
|
7830
|
-
return toXurlMentionsResponse(payload);
|
|
7752
|
+
const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
|
|
7753
|
+
const requestMaxResults = capTimelineCollectionMaxResults(
|
|
7754
|
+
collection,
|
|
7755
|
+
maxResults,
|
|
7756
|
+
isPaginatedWalk
|
|
7757
|
+
);
|
|
7758
|
+
const query = new URLSearchParams({
|
|
7759
|
+
max_results: String(requestMaxResults),
|
|
7760
|
+
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
7761
|
+
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
7762
|
+
"media.fields": MEDIA_FIELDS,
|
|
7763
|
+
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
7831
7764
|
});
|
|
7832
|
-
|
|
7765
|
+
if (paginationToken) {
|
|
7766
|
+
query.set("pagination_token", paginationToken);
|
|
7767
|
+
}
|
|
7768
|
+
const payload = yield* runOAuth2JsonCommandEffect({
|
|
7769
|
+
args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
|
|
7770
|
+
username
|
|
7771
|
+
});
|
|
7772
|
+
return toXurlMentionsResponse(payload);
|
|
7773
|
+
});
|
|
7833
7774
|
function listLikedTweetsViaXurlEffect(options) {
|
|
7834
7775
|
return listTimelineCollectionViaXurlEffect({
|
|
7835
7776
|
...options,
|
|
7836
7777
|
collection: "liked_tweets"
|
|
7837
7778
|
});
|
|
7838
7779
|
}
|
|
7839
|
-
function listLikedTweetsViaXurl(options) {
|
|
7840
|
-
return runEffectPromise(listLikedTweetsViaXurlEffect(options));
|
|
7841
|
-
}
|
|
7842
7780
|
function listBookmarkedTweetsViaXurlEffect(options) {
|
|
7843
7781
|
return listTimelineCollectionViaXurlEffect({
|
|
7844
7782
|
...options,
|
|
7845
7783
|
collection: "bookmarks"
|
|
7846
7784
|
});
|
|
7847
7785
|
}
|
|
7848
|
-
function listBookmarkedTweetsViaXurl(options) {
|
|
7849
|
-
return runEffectPromise(listBookmarkedTweetsViaXurlEffect(options));
|
|
7850
|
-
}
|
|
7851
7786
|
function listDirectMessageEventsViaXurlEffect({
|
|
7852
7787
|
maxResults,
|
|
7853
7788
|
username,
|
|
@@ -7869,19 +7804,19 @@ function listDirectMessageEventsViaXurlEffect({
|
|
|
7869
7804
|
}).pipe(
|
|
7870
7805
|
Effect9.map((payload) => ({
|
|
7871
7806
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
7872
|
-
includes: payload.includes && typeof payload.includes === "object" ? payload.includes : void 0,
|
|
7873
|
-
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
7874
|
-
}))
|
|
7875
|
-
);
|
|
7876
|
-
}
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7807
|
+
includes: payload.includes && typeof payload.includes === "object" ? payload.includes : void 0,
|
|
7808
|
+
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
7809
|
+
}))
|
|
7810
|
+
);
|
|
7811
|
+
}
|
|
7812
|
+
var listFollowUsersViaXurlEffect = Effect9.fn("xurl.listFollowUsers")(
|
|
7813
|
+
function* ({
|
|
7814
|
+
direction,
|
|
7815
|
+
maxResults,
|
|
7816
|
+
username,
|
|
7817
|
+
userId,
|
|
7818
|
+
paginationToken
|
|
7819
|
+
}) {
|
|
7885
7820
|
const resolvedUserId = yield* resolveUserIdEffect({ username, userId });
|
|
7886
7821
|
const query = new URLSearchParams({
|
|
7887
7822
|
max_results: String(maxResults),
|
|
@@ -7898,12 +7833,9 @@ function listFollowUsersViaXurlEffect({
|
|
|
7898
7833
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
7899
7834
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
7900
7835
|
};
|
|
7901
|
-
}
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
return runEffectPromise(listFollowUsersViaXurlEffect(options));
|
|
7905
|
-
}
|
|
7906
|
-
function listBlockedUsersEffect(userId, paginationToken) {
|
|
7836
|
+
}
|
|
7837
|
+
);
|
|
7838
|
+
var listBlockedUsersEffect = Effect9.fn("xurl.listBlockedUsers")((userId, paginationToken) => {
|
|
7907
7839
|
const query = new URLSearchParams({
|
|
7908
7840
|
max_results: "100",
|
|
7909
7841
|
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
@@ -7921,11 +7853,8 @@ function listBlockedUsersEffect(userId, paginationToken) {
|
|
|
7921
7853
|
};
|
|
7922
7854
|
})
|
|
7923
7855
|
);
|
|
7924
|
-
}
|
|
7925
|
-
|
|
7926
|
-
return runEffectPromise(listBlockedUsersEffect(userId, paginationToken));
|
|
7927
|
-
}
|
|
7928
|
-
function listUserTweetsEffect(userId, {
|
|
7856
|
+
});
|
|
7857
|
+
var listUserTweetsEffect = Effect9.fn("xurl.listUserTweets")((userId, {
|
|
7929
7858
|
maxResults,
|
|
7930
7859
|
paginationToken,
|
|
7931
7860
|
excludeRetweets = true,
|
|
@@ -7940,7 +7869,7 @@ function listUserTweetsEffect(userId, {
|
|
|
7940
7869
|
signal,
|
|
7941
7870
|
onAttempt,
|
|
7942
7871
|
useConfiguredCandidate
|
|
7943
|
-
}) {
|
|
7872
|
+
}) => {
|
|
7944
7873
|
const query = new URLSearchParams({
|
|
7945
7874
|
max_results: String(maxResults),
|
|
7946
7875
|
expansions: MEDIA_EXPANSION,
|
|
@@ -7987,7 +7916,7 @@ function listUserTweetsEffect(userId, {
|
|
|
7987
7916
|
};
|
|
7988
7917
|
})
|
|
7989
7918
|
);
|
|
7990
|
-
}
|
|
7919
|
+
});
|
|
7991
7920
|
function listUserTweets(userId, options) {
|
|
7992
7921
|
return runEffectPromise(listUserTweetsEffect(userId, options));
|
|
7993
7922
|
}
|
|
@@ -7998,7 +7927,7 @@ function toXurlTweetsResponse(payload) {
|
|
|
7998
7927
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
7999
7928
|
};
|
|
8000
7929
|
}
|
|
8001
|
-
|
|
7930
|
+
var lookupTweetsByIdsEffect = Effect9.fn("xurl.lookupTweetsByIds")((ids) => {
|
|
8002
7931
|
if (ids.length === 0) {
|
|
8003
7932
|
return Effect9.succeed({ data: [] });
|
|
8004
7933
|
}
|
|
@@ -8012,8 +7941,10 @@ function lookupTweetsByIdsEffect(ids) {
|
|
|
8012
7941
|
return runJsonCommandEffect([`/2/tweets?${query.toString()}`]).pipe(
|
|
8013
7942
|
Effect9.map(toXurlTweetsResponse)
|
|
8014
7943
|
);
|
|
8015
|
-
}
|
|
8016
|
-
|
|
7944
|
+
});
|
|
7945
|
+
var searchRecentByConversationIdEffect = Effect9.fn(
|
|
7946
|
+
"xurl.searchRecentByConversationId"
|
|
7947
|
+
)((conversationId, {
|
|
8017
7948
|
maxResults,
|
|
8018
7949
|
paginationToken,
|
|
8019
7950
|
timeoutMs,
|
|
@@ -8021,7 +7952,7 @@ function searchRecentByConversationIdEffect(conversationId, {
|
|
|
8021
7952
|
username,
|
|
8022
7953
|
signal,
|
|
8023
7954
|
onAttempt
|
|
8024
|
-
}) {
|
|
7955
|
+
}) => {
|
|
8025
7956
|
const query = new URLSearchParams({
|
|
8026
7957
|
query: `conversation_id:${conversationId}`,
|
|
8027
7958
|
max_results: String(maxResults),
|
|
@@ -8041,15 +7972,15 @@ function searchRecentByConversationIdEffect(conversationId, {
|
|
|
8041
7972
|
useConfiguredCandidate: false
|
|
8042
7973
|
}) : runJsonCommandEffect(args, { timeoutMs, signal, onAttempt });
|
|
8043
7974
|
return command.pipe(Effect9.map(toXurlTweetsResponse));
|
|
8044
|
-
}
|
|
8045
|
-
|
|
7975
|
+
});
|
|
7976
|
+
var searchRecentTweetsEffect = Effect9.fn("xurl.searchRecentTweets")((searchQuery, {
|
|
8046
7977
|
maxResults,
|
|
8047
7978
|
paginationToken,
|
|
8048
7979
|
startTime,
|
|
8049
7980
|
endTime,
|
|
8050
7981
|
username,
|
|
8051
7982
|
timeoutMs
|
|
8052
|
-
}) {
|
|
7983
|
+
}) => {
|
|
8053
7984
|
const query = new URLSearchParams({
|
|
8054
7985
|
query: searchQuery,
|
|
8055
7986
|
max_results: String(maxResults),
|
|
@@ -8073,8 +8004,8 @@ function searchRecentTweetsEffect(searchQuery, {
|
|
|
8073
8004
|
options: { timeoutMs },
|
|
8074
8005
|
useConfiguredCandidate: false
|
|
8075
8006
|
}).pipe(Effect9.map(toXurlTweetsResponse));
|
|
8076
|
-
}
|
|
8077
|
-
|
|
8007
|
+
});
|
|
8008
|
+
var getTweetByIdEffect = Effect9.fn("xurl.getTweetById")((id, { timeoutMs } = {}) => {
|
|
8078
8009
|
const query = new URLSearchParams({
|
|
8079
8010
|
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
8080
8011
|
"tweet.fields": THREAD_TWEET_FIELDS,
|
|
@@ -8093,7 +8024,7 @@ function getTweetByIdEffect(id, { timeoutMs } = {}) {
|
|
|
8093
8024
|
};
|
|
8094
8025
|
})
|
|
8095
8026
|
);
|
|
8096
|
-
}
|
|
8027
|
+
});
|
|
8097
8028
|
function postViaXurlEffect(text) {
|
|
8098
8029
|
return runShortcutEffect(["post", text]);
|
|
8099
8030
|
}
|
|
@@ -8116,9 +8047,6 @@ function blockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
8116
8047
|
JSON.stringify({ target_user_id: targetUserId })
|
|
8117
8048
|
]);
|
|
8118
8049
|
}
|
|
8119
|
-
function blockUserViaXurl(sourceUserId, targetUserId) {
|
|
8120
|
-
return runEffectPromise(blockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
8121
|
-
}
|
|
8122
8050
|
function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
8123
8051
|
return runMutationCommandEffect([
|
|
8124
8052
|
"-X",
|
|
@@ -8126,9 +8054,6 @@ function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
8126
8054
|
`/2/users/${sourceUserId}/blocking/${targetUserId}`
|
|
8127
8055
|
]);
|
|
8128
8056
|
}
|
|
8129
|
-
function unblockUserViaXurl(sourceUserId, targetUserId) {
|
|
8130
|
-
return runEffectPromise(unblockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
8131
|
-
}
|
|
8132
8057
|
function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
8133
8058
|
return runMutationCommandEffect([
|
|
8134
8059
|
"-X",
|
|
@@ -8138,9 +8063,6 @@ function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
8138
8063
|
JSON.stringify({ target_user_id: targetUserId })
|
|
8139
8064
|
]);
|
|
8140
8065
|
}
|
|
8141
|
-
function muteUserViaXurl(sourceUserId, targetUserId) {
|
|
8142
|
-
return runEffectPromise(muteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
8143
|
-
}
|
|
8144
8066
|
function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
8145
8067
|
return runMutationCommandEffect([
|
|
8146
8068
|
"-X",
|
|
@@ -8148,49 +8070,40 @@ function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
8148
8070
|
`/2/users/${sourceUserId}/muting/${targetUserId}`
|
|
8149
8071
|
]);
|
|
8150
8072
|
}
|
|
8151
|
-
function unmuteUserViaXurl(sourceUserId, targetUserId) {
|
|
8152
|
-
return runEffectPromise(unmuteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
8153
|
-
}
|
|
8154
8073
|
|
|
8155
8074
|
// src/lib/live-transport-gateway.ts
|
|
8156
|
-
function fromPromise(run) {
|
|
8157
|
-
return Effect10.tryPromise({
|
|
8158
|
-
try: run,
|
|
8159
|
-
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
8160
|
-
});
|
|
8161
|
-
}
|
|
8162
8075
|
var liveTransportGateway = {
|
|
8163
8076
|
bird: {
|
|
8164
|
-
getAuthenticatedAccount: () => getAuthenticatedBirdAccountEffect(),
|
|
8165
|
-
listBookmarks: (
|
|
8166
|
-
listDirectMessages: (
|
|
8167
|
-
listFollowUsers: (
|
|
8168
|
-
listHomeTimeline: (
|
|
8169
|
-
listLikes: (
|
|
8170
|
-
listMentions: (
|
|
8171
|
-
listThread: (
|
|
8172
|
-
searchTweets: (
|
|
8077
|
+
getAuthenticatedAccount: (...args) => getAuthenticatedBirdAccountEffect(...args),
|
|
8078
|
+
listBookmarks: (...args) => listBookmarkedTweetsViaBirdEffect(...args),
|
|
8079
|
+
listDirectMessages: (...args) => listDirectMessagesViaBirdEffect(...args),
|
|
8080
|
+
listFollowUsers: (...args) => listFollowUsersViaBirdEffect(...args),
|
|
8081
|
+
listHomeTimeline: (...args) => listHomeTimelineViaBirdEffect(...args),
|
|
8082
|
+
listLikes: (...args) => listLikedTweetsViaBirdEffect(...args),
|
|
8083
|
+
listMentions: (...args) => listMentionsViaBirdEffect(...args),
|
|
8084
|
+
listThread: (...args) => listThreadViaBirdEffect(...args),
|
|
8085
|
+
searchTweets: (...args) => searchTweetsViaBirdEffect(...args)
|
|
8173
8086
|
},
|
|
8174
8087
|
xurl: {
|
|
8175
|
-
getTransportStatus: (...args) =>
|
|
8176
|
-
getTweetById: (
|
|
8177
|
-
listBookmarks: (...args) =>
|
|
8178
|
-
listDirectMessages: (
|
|
8179
|
-
listFollowUsers: (...args) =>
|
|
8180
|
-
listHomeTimeline: (
|
|
8181
|
-
listLikes: (...args) =>
|
|
8182
|
-
listMentions: (...args) =>
|
|
8183
|
-
listUserTweets: (...args) =>
|
|
8184
|
-
lookupAuthenticatedOAuth2User: (
|
|
8185
|
-
lookupAuthenticatedUser: (...args) =>
|
|
8186
|
-
lookupUsersByHandles: (...args) =>
|
|
8187
|
-
searchConversation: (
|
|
8188
|
-
searchRecentTweets: (
|
|
8088
|
+
getTransportStatus: (...args) => getTransportStatusEffect(...args),
|
|
8089
|
+
getTweetById: (...args) => getTweetByIdEffect(...args),
|
|
8090
|
+
listBookmarks: (...args) => listBookmarkedTweetsViaXurlEffect(...args),
|
|
8091
|
+
listDirectMessages: (...args) => listDirectMessageEventsViaXurlEffect(...args),
|
|
8092
|
+
listFollowUsers: (...args) => listFollowUsersViaXurlEffect(...args),
|
|
8093
|
+
listHomeTimeline: (...args) => listHomeTimelineViaXurlEffect(...args),
|
|
8094
|
+
listLikes: (...args) => listLikedTweetsViaXurlEffect(...args),
|
|
8095
|
+
listMentions: (...args) => listMentionsViaXurlEffect(...args),
|
|
8096
|
+
listUserTweets: (...args) => listUserTweetsEffect(...args),
|
|
8097
|
+
lookupAuthenticatedOAuth2User: (...args) => lookupAuthenticatedOAuth2UserEffect(...args),
|
|
8098
|
+
lookupAuthenticatedUser: (...args) => lookupAuthenticatedUserEffect(...args),
|
|
8099
|
+
lookupUsersByHandles: (...args) => lookupUsersByHandlesEffect(...args),
|
|
8100
|
+
searchConversation: (...args) => searchRecentByConversationIdEffect(...args),
|
|
8101
|
+
searchRecentTweets: (...args) => searchRecentTweetsEffect(...args)
|
|
8189
8102
|
}
|
|
8190
8103
|
};
|
|
8191
8104
|
|
|
8192
8105
|
// src/lib/live-sync-engine.ts
|
|
8193
|
-
import { Effect as
|
|
8106
|
+
import { Effect as Effect10 } from "effect";
|
|
8194
8107
|
|
|
8195
8108
|
// src/lib/sync-cache.ts
|
|
8196
8109
|
function readSyncCacheRow(cacheKey3, db = getNativeDb()) {
|
|
@@ -8259,7 +8172,7 @@ function resolveLiveSyncAccount(db, accountId) {
|
|
|
8259
8172
|
function createLiveTransportAdapter(source, fetch2) {
|
|
8260
8173
|
return {
|
|
8261
8174
|
source,
|
|
8262
|
-
fetch: fetch2.pipe(
|
|
8175
|
+
fetch: fetch2.pipe(Effect10.mapError(toError))
|
|
8263
8176
|
};
|
|
8264
8177
|
}
|
|
8265
8178
|
function assertLiveAccountMatches({
|
|
@@ -8291,12 +8204,12 @@ function normalizeCacheTtlMs(value, defaultValue) {
|
|
|
8291
8204
|
function fetchWithTransportFallbackEffect(transports) {
|
|
8292
8205
|
const [first, ...rest] = transports;
|
|
8293
8206
|
if (!first) {
|
|
8294
|
-
return
|
|
8207
|
+
return Effect10.fail(new Error("No live transport adapters configured"));
|
|
8295
8208
|
}
|
|
8296
8209
|
return first.fetch.pipe(
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
(error) => rest.length > 0 ? fetchWithTransportFallbackEffect(rest) :
|
|
8210
|
+
Effect10.map((payload) => ({ source: first.source, payload })),
|
|
8211
|
+
Effect10.catchAll(
|
|
8212
|
+
(error) => rest.length > 0 ? fetchWithTransportFallbackEffect(rest) : Effect10.fail(toError(error))
|
|
8300
8213
|
)
|
|
8301
8214
|
);
|
|
8302
8215
|
}
|
|
@@ -8309,8 +8222,8 @@ function runCachedLiveSyncEffect({
|
|
|
8309
8222
|
persistLive,
|
|
8310
8223
|
persistCached
|
|
8311
8224
|
}) {
|
|
8312
|
-
return
|
|
8313
|
-
const cached = yield*
|
|
8225
|
+
return Effect10.gen(function* () {
|
|
8226
|
+
const cached = yield* Effect10.try({
|
|
8314
8227
|
try: () => readSyncCache(cacheKey3, db),
|
|
8315
8228
|
catch: toError
|
|
8316
8229
|
});
|
|
@@ -8340,7 +8253,7 @@ function runCachedLiveSyncEffect({
|
|
|
8340
8253
|
}
|
|
8341
8254
|
|
|
8342
8255
|
// src/lib/sync-plan.ts
|
|
8343
|
-
import { Effect as
|
|
8256
|
+
import { Effect as Effect11 } from "effect";
|
|
8344
8257
|
function normalizeCursor(value) {
|
|
8345
8258
|
const normalized = value?.trim();
|
|
8346
8259
|
return normalized || void 0;
|
|
@@ -8358,7 +8271,7 @@ function runSyncPlanEffect({
|
|
|
8358
8271
|
shouldStop,
|
|
8359
8272
|
allowPartialFailure = false
|
|
8360
8273
|
}) {
|
|
8361
|
-
return
|
|
8274
|
+
return Effect11.gen(function* () {
|
|
8362
8275
|
const pages = [];
|
|
8363
8276
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
8364
8277
|
let cursor = normalizeCursor(initialCursor);
|
|
@@ -8369,14 +8282,14 @@ function runSyncPlanEffect({
|
|
|
8369
8282
|
while (pages.length < pageLimit) {
|
|
8370
8283
|
const pageIndex = pages.length;
|
|
8371
8284
|
const outcome = yield* fetchPage({ cursor, fetched, pageIndex }).pipe(
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
(error) =>
|
|
8285
|
+
Effect11.map((page2) => ({ ok: true, page: page2 })),
|
|
8286
|
+
Effect11.catchAll(
|
|
8287
|
+
(error) => Effect11.succeed({ ok: false, error })
|
|
8375
8288
|
)
|
|
8376
8289
|
);
|
|
8377
8290
|
if (!outcome.ok) {
|
|
8378
8291
|
if (!allowPartialFailure || pages.length === 0) {
|
|
8379
|
-
return yield*
|
|
8292
|
+
return yield* Effect11.fail(outcome.error);
|
|
8380
8293
|
}
|
|
8381
8294
|
return {
|
|
8382
8295
|
complete: false,
|
|
@@ -8432,7 +8345,7 @@ function runSyncPlanEffect({
|
|
|
8432
8345
|
cursor = nextCursor;
|
|
8433
8346
|
seenCursors.add(nextCursor);
|
|
8434
8347
|
if (typeof pageDelayMs === "number" && pageDelayMs > 0) {
|
|
8435
|
-
yield*
|
|
8348
|
+
yield* Effect11.sleep(pageDelayMs);
|
|
8436
8349
|
}
|
|
8437
8350
|
}
|
|
8438
8351
|
return {
|
|
@@ -8852,7 +8765,7 @@ function renderTweetMarkdown(text, entities) {
|
|
|
8852
8765
|
import { createHash as createHash2 } from "node:crypto";
|
|
8853
8766
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8854
8767
|
import path3 from "node:path";
|
|
8855
|
-
import { Effect as
|
|
8768
|
+
import { Effect as Effect12 } from "effect";
|
|
8856
8769
|
var AVATAR_SIZE_SUFFIX = /(?:(?:_normal|_bigger|_mini))(?=\.(?:jpg|jpeg|png|webp|gif)(?:$|\?))/i;
|
|
8857
8770
|
var MAX_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
8858
8771
|
var MAX_AVATAR_DATA_URL_CHARS = MAX_AVATAR_BYTES * 4;
|
|
@@ -8940,7 +8853,7 @@ function toError5(error) {
|
|
|
8940
8853
|
return error instanceof Error ? error : new Error(String(error));
|
|
8941
8854
|
}
|
|
8942
8855
|
function trySync3(try_) {
|
|
8943
|
-
return
|
|
8856
|
+
return Effect12.try({
|
|
8944
8857
|
try: try_,
|
|
8945
8858
|
catch: toError5
|
|
8946
8859
|
});
|
|
@@ -9007,7 +8920,7 @@ function getAvatarCachePath(profileId, avatarUrl) {
|
|
|
9007
8920
|
);
|
|
9008
8921
|
}
|
|
9009
8922
|
function fetchRemoteAvatarEffect(avatarUrl) {
|
|
9010
|
-
return
|
|
8923
|
+
return Effect12.gen(function* () {
|
|
9011
8924
|
const safeUrl = yield* trySync3(() => assertSafeRemoteAvatarUrl(avatarUrl));
|
|
9012
8925
|
const response = yield* tryPromise(
|
|
9013
8926
|
() => fetch(safeUrl, {
|
|
@@ -9019,13 +8932,13 @@ function fetchRemoteAvatarEffect(avatarUrl) {
|
|
|
9019
8932
|
})
|
|
9020
8933
|
);
|
|
9021
8934
|
if (!response.ok) {
|
|
9022
|
-
return yield*
|
|
8935
|
+
return yield* Effect12.fail(
|
|
9023
8936
|
new Error(`Avatar fetch failed with ${response.status}`)
|
|
9024
8937
|
);
|
|
9025
8938
|
}
|
|
9026
8939
|
const buffer = Buffer.from(yield* tryPromise(() => response.arrayBuffer()));
|
|
9027
8940
|
if (buffer.byteLength > MAX_AVATAR_BYTES) {
|
|
9028
|
-
return yield*
|
|
8941
|
+
return yield* Effect12.fail(new Error("Avatar response is too large"));
|
|
9029
8942
|
}
|
|
9030
8943
|
const contentType = yield* trySync3(
|
|
9031
8944
|
() => detectRasterContentType(
|
|
@@ -9040,7 +8953,7 @@ function fetchRemoteAvatarEffect(avatarUrl) {
|
|
|
9040
8953
|
});
|
|
9041
8954
|
}
|
|
9042
8955
|
function readCachedAvatarEffect(profileId) {
|
|
9043
|
-
return
|
|
8956
|
+
return Effect12.gen(function* () {
|
|
9044
8957
|
const avatarUrl = yield* trySync3(() => getAvatarUrlForProfile(profileId));
|
|
9045
8958
|
if (!avatarUrl) {
|
|
9046
8959
|
return null;
|
|
@@ -9054,8 +8967,8 @@ function readCachedAvatarEffect(profileId) {
|
|
|
9054
8967
|
);
|
|
9055
8968
|
const cachedExtension = path3.extname(cachePath);
|
|
9056
8969
|
const cached = yield* trySync3(() => readFileSync3(cachePath)).pipe(
|
|
9057
|
-
|
|
9058
|
-
|
|
8970
|
+
Effect12.map((buffer) => ({ ok: true, buffer })),
|
|
8971
|
+
Effect12.catchAll(() => Effect12.succeed({ ok: false }))
|
|
9059
8972
|
);
|
|
9060
8973
|
if (cached.ok) {
|
|
9061
8974
|
return {
|
|
@@ -9088,17 +9001,17 @@ function prefetchCachedAvatarsForProfileIdsEffect(profileIds, options = {}) {
|
|
|
9088
9001
|
)
|
|
9089
9002
|
);
|
|
9090
9003
|
const concurrency = normalizePrefetchConcurrency(options.concurrency);
|
|
9091
|
-
return
|
|
9004
|
+
return Effect12.forEach(
|
|
9092
9005
|
uniqueProfileIds,
|
|
9093
9006
|
(profileId) => readCachedAvatarEffect(profileId).pipe(
|
|
9094
|
-
|
|
9007
|
+
Effect12.map(
|
|
9095
9008
|
(avatar) => avatar ? "available" : "missing"
|
|
9096
9009
|
),
|
|
9097
|
-
|
|
9010
|
+
Effect12.catchAll(() => Effect12.succeed("failed"))
|
|
9098
9011
|
),
|
|
9099
9012
|
{ concurrency }
|
|
9100
9013
|
).pipe(
|
|
9101
|
-
|
|
9014
|
+
Effect12.map((statuses) => ({
|
|
9102
9015
|
requested: uniqueProfileIds.length,
|
|
9103
9016
|
available: statuses.filter((status) => status === "available").length,
|
|
9104
9017
|
missing: statuses.filter((status) => status === "missing").length,
|
|
@@ -10507,10 +10420,10 @@ function fetchConversationViaRecentSearchEffect({
|
|
|
10507
10420
|
timeoutMs,
|
|
10508
10421
|
deadlineMs
|
|
10509
10422
|
}) {
|
|
10510
|
-
return
|
|
10423
|
+
return Effect13.gen(function* () {
|
|
10511
10424
|
const result = yield* runSyncPlanEffect({
|
|
10512
10425
|
fetchPage: ({ cursor }) => trySync(() => getRemainingThreadTimeoutMs(deadlineMs, timeoutMs)).pipe(
|
|
10513
|
-
|
|
10426
|
+
Effect13.flatMap(
|
|
10514
10427
|
(remainingTimeoutMs) => liveTransportGateway.xurl.searchConversation(conversationId, {
|
|
10515
10428
|
maxResults: MAX_XURL_SEARCH_RESULTS,
|
|
10516
10429
|
paginationToken: cursor,
|
|
@@ -10537,7 +10450,7 @@ function fetchParentChainViaXurlEffect({
|
|
|
10537
10450
|
timeoutMs,
|
|
10538
10451
|
deadlineMs
|
|
10539
10452
|
}) {
|
|
10540
|
-
return
|
|
10453
|
+
return Effect13.gen(function* () {
|
|
10541
10454
|
const pages = [];
|
|
10542
10455
|
const warnings = [];
|
|
10543
10456
|
const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
|
|
@@ -10635,7 +10548,7 @@ function fetchThreadContextViaXurlEffect({
|
|
|
10635
10548
|
maxFallbackDepth,
|
|
10636
10549
|
timeoutMs
|
|
10637
10550
|
}) {
|
|
10638
|
-
return
|
|
10551
|
+
return Effect13.gen(function* () {
|
|
10639
10552
|
const deadlineMs = Date.now() + timeoutMs;
|
|
10640
10553
|
if (!mention.conversationId) {
|
|
10641
10554
|
if (mention.replyToId) {
|
|
@@ -10735,7 +10648,7 @@ function syncMentionThreadsEffect({
|
|
|
10735
10648
|
maxPages,
|
|
10736
10649
|
onProgress
|
|
10737
10650
|
}) {
|
|
10738
|
-
return
|
|
10651
|
+
return Effect13.gen(function* () {
|
|
10739
10652
|
const parsedMode = yield* trySync(() => parseMode(mode));
|
|
10740
10653
|
const parsedLimit = yield* trySync(
|
|
10741
10654
|
() => assertPositiveInteger(limit, "--limit")
|
|
@@ -10768,7 +10681,7 @@ function syncMentionThreadsEffect({
|
|
|
10768
10681
|
const warnings = [];
|
|
10769
10682
|
for (const [index, mention] of mentions.entries()) {
|
|
10770
10683
|
if (index > 0 && parsedDelayMs > 0) {
|
|
10771
|
-
yield*
|
|
10684
|
+
yield* Effect13.sleep(parsedDelayMs);
|
|
10772
10685
|
}
|
|
10773
10686
|
const fetchEffect = parsedMode === "bird" ? liveTransportGateway.bird.listThread({
|
|
10774
10687
|
tweetId: mention.id,
|
|
@@ -10776,7 +10689,7 @@ function syncMentionThreadsEffect({
|
|
|
10776
10689
|
maxPages: parsedMaxPages,
|
|
10777
10690
|
timeoutMs: parsedTimeoutMs
|
|
10778
10691
|
}).pipe(
|
|
10779
|
-
|
|
10692
|
+
Effect13.map((payload2) => ({
|
|
10780
10693
|
strategy: "bird",
|
|
10781
10694
|
payload: payload2,
|
|
10782
10695
|
pages: void 0,
|
|
@@ -10793,7 +10706,7 @@ function syncMentionThreadsEffect({
|
|
|
10793
10706
|
timeoutMs: parsedTimeoutMs
|
|
10794
10707
|
});
|
|
10795
10708
|
const fetched = yield* fetchEffect.pipe(
|
|
10796
|
-
|
|
10709
|
+
Effect13.flatMap(
|
|
10797
10710
|
(fetchResult2) => databaseWriteEffect(
|
|
10798
10711
|
(writeDb) => mergeMentionThreadIntoLocalStore({
|
|
10799
10712
|
db: writeDb,
|
|
@@ -10804,11 +10717,11 @@ function syncMentionThreadsEffect({
|
|
|
10804
10717
|
source: parsedMode,
|
|
10805
10718
|
writeThreadContextEdges: parsedMode === "xurl"
|
|
10806
10719
|
})
|
|
10807
|
-
).pipe(
|
|
10720
|
+
).pipe(Effect13.as(fetchResult2))
|
|
10808
10721
|
),
|
|
10809
|
-
|
|
10810
|
-
|
|
10811
|
-
(error) =>
|
|
10722
|
+
Effect13.map((fetchResult2) => ({ ok: true, fetchResult: fetchResult2 })),
|
|
10723
|
+
Effect13.catchAll(
|
|
10724
|
+
(error) => Effect13.succeed({ ok: false, error })
|
|
10812
10725
|
)
|
|
10813
10726
|
);
|
|
10814
10727
|
if (!fetched.ok) {
|
|
@@ -10820,7 +10733,7 @@ function syncMentionThreadsEffect({
|
|
|
10820
10733
|
strategy: parsedMode,
|
|
10821
10734
|
error: fetched.error instanceof Error ? fetched.error.message : String(fetched.error)
|
|
10822
10735
|
});
|
|
10823
|
-
yield*
|
|
10736
|
+
yield* Effect13.sync(
|
|
10824
10737
|
() => onProgress?.({
|
|
10825
10738
|
source: parsedMode,
|
|
10826
10739
|
processed: index + 1,
|
|
@@ -10850,7 +10763,7 @@ function syncMentionThreadsEffect({
|
|
|
10850
10763
|
truncated: fetchResult.truncated,
|
|
10851
10764
|
warnings: fetchResult.warnings.length > 0 ? fetchResult.warnings : void 0
|
|
10852
10765
|
});
|
|
10853
|
-
yield*
|
|
10766
|
+
yield* Effect13.sync(
|
|
10854
10767
|
() => onProgress?.({
|
|
10855
10768
|
source: parsedMode,
|
|
10856
10769
|
processed: index + 1,
|
|
@@ -10899,7 +10812,7 @@ function syncMentionThreads(options) {
|
|
|
10899
10812
|
}
|
|
10900
10813
|
|
|
10901
10814
|
// src/lib/mentions-live.ts
|
|
10902
|
-
import { Effect as
|
|
10815
|
+
import { Effect as Effect14 } from "effect";
|
|
10903
10816
|
|
|
10904
10817
|
// src/lib/query-read-model-shared.ts
|
|
10905
10818
|
function parseJsonField2(value, fallback) {
|
|
@@ -12070,9 +11983,9 @@ function writeMentionHighWaterId(db, mode, accountId, sinceId) {
|
|
|
12070
11983
|
writeSyncCache(getMentionHighWaterKey({ mode, accountId }), { sinceId }, db);
|
|
12071
11984
|
}
|
|
12072
11985
|
function verifyBirdAccountMatchesEffect(account) {
|
|
12073
|
-
return
|
|
11986
|
+
return Effect14.gen(function* () {
|
|
12074
11987
|
const authenticated = yield* liveTransportGateway.bird.getAuthenticatedAccount();
|
|
12075
|
-
return yield*
|
|
11988
|
+
return yield* Effect14.try({
|
|
12076
11989
|
try: () => assertLiveAccountMatches({
|
|
12077
11990
|
source: "bird",
|
|
12078
11991
|
account,
|
|
@@ -12188,10 +12101,10 @@ function fetchMentionsViaXurlEffect({
|
|
|
12188
12101
|
startTime,
|
|
12189
12102
|
onProgress
|
|
12190
12103
|
}) {
|
|
12191
|
-
return
|
|
12192
|
-
const accountUserId = resolvedAccount.externalUserId ?? (yield* liveTransportGateway.xurl.lookupUsersByHandles([resolvedAccount.username]).pipe(
|
|
12104
|
+
return Effect14.gen(function* () {
|
|
12105
|
+
const accountUserId = resolvedAccount.externalUserId ?? (yield* liveTransportGateway.xurl.lookupUsersByHandles([resolvedAccount.username]).pipe(Effect14.map((users) => users[0]?.id)));
|
|
12193
12106
|
if (!accountUserId) {
|
|
12194
|
-
return yield*
|
|
12107
|
+
return yield* Effect14.fail(
|
|
12195
12108
|
new Error(
|
|
12196
12109
|
`Could not resolve Twitter user id for @${resolvedAccount.username}`
|
|
12197
12110
|
)
|
|
@@ -12243,13 +12156,13 @@ function syncMentionsEffect({
|
|
|
12243
12156
|
startTime,
|
|
12244
12157
|
onProgress
|
|
12245
12158
|
}) {
|
|
12246
|
-
return
|
|
12159
|
+
return Effect14.gen(function* () {
|
|
12247
12160
|
const parsedMode = yield* trySync(() => parseSyncMode(mode));
|
|
12248
12161
|
const primaryMode = parsedMode === "auto" ? "xurl" : parsedMode;
|
|
12249
12162
|
const explicitSinceId = sinceId?.trim() || void 0;
|
|
12250
12163
|
const explicitStartTime = startTime?.trim() || void 0;
|
|
12251
12164
|
if (primaryMode === "bird" && (explicitSinceId || explicitStartTime)) {
|
|
12252
|
-
return yield*
|
|
12165
|
+
return yield* Effect14.fail(
|
|
12253
12166
|
new Error("bird mode does not support --since-id or --start-time")
|
|
12254
12167
|
);
|
|
12255
12168
|
}
|
|
@@ -12325,7 +12238,7 @@ function syncMentionsEffect({
|
|
|
12325
12238
|
primaryMode
|
|
12326
12239
|
)
|
|
12327
12240
|
);
|
|
12328
|
-
yield*
|
|
12241
|
+
yield* Effect14.sync(
|
|
12329
12242
|
() => onProgress?.({
|
|
12330
12243
|
source: "cache",
|
|
12331
12244
|
fetched: cached.value.data.length,
|
|
@@ -12363,16 +12276,16 @@ function syncMentionsEffect({
|
|
|
12363
12276
|
startTime: resolvedStartTime,
|
|
12364
12277
|
onProgress
|
|
12365
12278
|
}).pipe(
|
|
12366
|
-
|
|
12367
|
-
if (!canFallbackToBird) return
|
|
12279
|
+
Effect14.catchAll((error) => {
|
|
12280
|
+
if (!canFallbackToBird) return Effect14.fail(error);
|
|
12368
12281
|
source = "bird";
|
|
12369
12282
|
return verifyBirdAccountMatchesEffect(resolvedAccount).pipe(
|
|
12370
|
-
|
|
12283
|
+
Effect14.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
|
|
12371
12284
|
);
|
|
12372
12285
|
})
|
|
12373
12286
|
);
|
|
12374
12287
|
if (source === "bird") {
|
|
12375
|
-
yield*
|
|
12288
|
+
yield* Effect14.sync(
|
|
12376
12289
|
() => onProgress?.({
|
|
12377
12290
|
source: "bird",
|
|
12378
12291
|
fetched: payload.data.length,
|
|
@@ -12475,7 +12388,7 @@ function exportMentionsViaCachedLiveSourceEffect({
|
|
|
12475
12388
|
refresh = false,
|
|
12476
12389
|
cacheTtlMs
|
|
12477
12390
|
}) {
|
|
12478
|
-
return
|
|
12391
|
+
return Effect14.gen(function* () {
|
|
12479
12392
|
const primaryMode = mode === "auto" ? "xurl" : mode;
|
|
12480
12393
|
if (primaryMode === "xurl") {
|
|
12481
12394
|
yield* trySync(() => assertXurlLimit(limit));
|
|
@@ -12527,14 +12440,14 @@ function exportMentionsViaCachedLiveSourceEffect({
|
|
|
12527
12440
|
all: fetchAll,
|
|
12528
12441
|
parsedMaxPages
|
|
12529
12442
|
})).pipe(
|
|
12530
|
-
|
|
12531
|
-
if (mode !== "auto" || fetchAll) return
|
|
12443
|
+
Effect14.catchAll((error) => {
|
|
12444
|
+
if (mode !== "auto" || fetchAll) return Effect14.fail(error);
|
|
12532
12445
|
source = "bird";
|
|
12533
12446
|
return verifyBirdAccountMatchesEffect(resolvedAccount).pipe(
|
|
12534
|
-
|
|
12447
|
+
Effect14.flatMap(() => fetchMentionsViaBirdEffect({ limit }))
|
|
12535
12448
|
);
|
|
12536
12449
|
}),
|
|
12537
|
-
|
|
12450
|
+
Effect14.flatMap(
|
|
12538
12451
|
(payload) => databaseWriteEffect((writeDb) => {
|
|
12539
12452
|
mergeMentionsIntoLocalStore(
|
|
12540
12453
|
writeDb,
|
|
@@ -12546,17 +12459,17 @@ function exportMentionsViaCachedLiveSourceEffect({
|
|
|
12546
12459
|
return readFilteredOrRaw(payload);
|
|
12547
12460
|
})
|
|
12548
12461
|
),
|
|
12549
|
-
|
|
12550
|
-
|
|
12462
|
+
Effect14.map((payload) => ({ ok: true, payload })),
|
|
12463
|
+
Effect14.catchAll((error) => {
|
|
12551
12464
|
if (!refresh && cached) {
|
|
12552
|
-
return
|
|
12465
|
+
return Effect14.succeed({ ok: false });
|
|
12553
12466
|
}
|
|
12554
|
-
return
|
|
12467
|
+
return Effect14.fail(error);
|
|
12555
12468
|
})
|
|
12556
12469
|
);
|
|
12557
12470
|
if (!liveResult.ok) {
|
|
12558
12471
|
if (!cached) {
|
|
12559
|
-
return yield*
|
|
12472
|
+
return yield* Effect14.fail(
|
|
12560
12473
|
new Error("Mention export failed without cache")
|
|
12561
12474
|
);
|
|
12562
12475
|
}
|
|
@@ -13033,7 +12946,7 @@ function getDmSearchMatches({
|
|
|
13033
12946
|
}
|
|
13034
12947
|
|
|
13035
12948
|
// src/lib/timeline-live.ts
|
|
13036
|
-
import { Effect as
|
|
12949
|
+
import { Effect as Effect15 } from "effect";
|
|
13037
12950
|
var DEFAULT_TIMELINE_CACHE_TTL_MS = 2 * 6e4;
|
|
13038
12951
|
var MAX_XURL_TIMELINE_PAGE_SIZE = 100;
|
|
13039
12952
|
function assertLimit(limit) {
|
|
@@ -13124,8 +13037,8 @@ function syncHomeTimelineEffect({
|
|
|
13124
13037
|
timeoutMs,
|
|
13125
13038
|
onProgress
|
|
13126
13039
|
} = {}) {
|
|
13127
|
-
return
|
|
13128
|
-
const parsedStartTime = yield*
|
|
13040
|
+
return Effect15.gen(function* () {
|
|
13041
|
+
const parsedStartTime = yield* Effect15.try({
|
|
13129
13042
|
try: () => parseStartTime(startTime),
|
|
13130
13043
|
catch: (error) => error
|
|
13131
13044
|
});
|
|
@@ -13139,9 +13052,9 @@ function syncHomeTimelineEffect({
|
|
|
13139
13052
|
const accountId = resolvedAccount.accountId;
|
|
13140
13053
|
const effectiveMode = parsedMode === "auto" && account !== void 0 && !resolvedAccount.isDefault ? "xurl" : parsedMode;
|
|
13141
13054
|
const cacheKey3 = `timeline:${effectiveMode}:${accountId}:${following ? "following" : "for-you"}:${Number.isFinite(effectiveLimit) ? String(effectiveLimit) : "all"}:${Number.isFinite(parsedMaxPages) ? String(parsedMaxPages) : "all-pages"}:${parsedStartTime?.iso ?? "no-start"}`;
|
|
13142
|
-
const fetchViaXurl =
|
|
13055
|
+
const fetchViaXurl = Effect15.gen(function* () {
|
|
13143
13056
|
if (!following) {
|
|
13144
|
-
return yield*
|
|
13057
|
+
return yield* Effect15.fail(
|
|
13145
13058
|
new Error("xurl home timeline mode does not support --for-you")
|
|
13146
13059
|
);
|
|
13147
13060
|
}
|
|
@@ -13205,7 +13118,7 @@ function syncHomeTimelineEffect({
|
|
|
13205
13118
|
});
|
|
13206
13119
|
const { source, payload } = syncResult;
|
|
13207
13120
|
if (source === "cache") {
|
|
13208
|
-
yield*
|
|
13121
|
+
yield* Effect15.sync(
|
|
13209
13122
|
() => onProgress?.({
|
|
13210
13123
|
source: "cache",
|
|
13211
13124
|
fetched: payload.data.length,
|
|
@@ -13215,7 +13128,7 @@ function syncHomeTimelineEffect({
|
|
|
13215
13128
|
);
|
|
13216
13129
|
}
|
|
13217
13130
|
if (source === "bird") {
|
|
13218
|
-
yield*
|
|
13131
|
+
yield* Effect15.sync(
|
|
13219
13132
|
() => onProgress?.({
|
|
13220
13133
|
source: "bird",
|
|
13221
13134
|
fetched: payload.data.length,
|
|
@@ -13309,7 +13222,7 @@ function toError6(error) {
|
|
|
13309
13222
|
return error instanceof Error ? error : new Error(String(error));
|
|
13310
13223
|
}
|
|
13311
13224
|
function tryDigestSync(try_) {
|
|
13312
|
-
return
|
|
13225
|
+
return Effect16.try({
|
|
13313
13226
|
try: try_,
|
|
13314
13227
|
catch: toError6
|
|
13315
13228
|
});
|
|
@@ -13682,7 +13595,7 @@ function formatPageDetail({
|
|
|
13682
13595
|
}
|
|
13683
13596
|
function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
13684
13597
|
if (!options.liveSync) {
|
|
13685
|
-
return
|
|
13598
|
+
return Effect16.void;
|
|
13686
13599
|
}
|
|
13687
13600
|
const includeTimeline = phase.timeline ?? true;
|
|
13688
13601
|
const includeMentions = phase.mentions ?? true;
|
|
@@ -13707,9 +13620,9 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13707
13620
|
);
|
|
13708
13621
|
const timelineMaxPages = options.liveTimelineMaxPages === void 0 ? DEFAULT_LIVE_TIMELINE_MAX_PAGES : boundedPositiveInteger(options.liveTimelineMaxPages, 3, 1e3);
|
|
13709
13622
|
const mentionsMaxPages = options.liveMentionsMaxPages === void 0 ? DEFAULT_LIVE_MENTIONS_MAX_PAGES : boundedPositiveInteger(options.liveMentionsMaxPages, 3, 1e3);
|
|
13710
|
-
return
|
|
13623
|
+
return Effect16.gen(function* () {
|
|
13711
13624
|
if (includeTimeline) {
|
|
13712
|
-
yield*
|
|
13625
|
+
yield* Effect16.sync(
|
|
13713
13626
|
() => emitDigestStatus(
|
|
13714
13627
|
handlers,
|
|
13715
13628
|
"Fetching home timeline from X",
|
|
@@ -13741,12 +13654,12 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13741
13654
|
})
|
|
13742
13655
|
)
|
|
13743
13656
|
}).pipe(
|
|
13744
|
-
|
|
13657
|
+
Effect16.match({
|
|
13745
13658
|
onFailure: () => null,
|
|
13746
13659
|
onSuccess: (value) => value
|
|
13747
13660
|
})
|
|
13748
13661
|
);
|
|
13749
|
-
yield*
|
|
13662
|
+
yield* Effect16.sync(
|
|
13750
13663
|
() => emitDigestStatus(
|
|
13751
13664
|
handlers,
|
|
13752
13665
|
result ? `Fetched ${String(result.count)} home tweets from ${result.source}` : "Home timeline fetch failed; using local data"
|
|
@@ -13754,7 +13667,7 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13754
13667
|
);
|
|
13755
13668
|
}
|
|
13756
13669
|
if (includeMentions) {
|
|
13757
|
-
yield*
|
|
13670
|
+
yield* Effect16.sync(
|
|
13758
13671
|
() => emitDigestStatus(
|
|
13759
13672
|
handlers,
|
|
13760
13673
|
"Fetching mentions from X",
|
|
@@ -13784,12 +13697,12 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13784
13697
|
})
|
|
13785
13698
|
)
|
|
13786
13699
|
}).pipe(
|
|
13787
|
-
|
|
13700
|
+
Effect16.match({
|
|
13788
13701
|
onFailure: () => null,
|
|
13789
13702
|
onSuccess: (value) => value
|
|
13790
13703
|
})
|
|
13791
13704
|
);
|
|
13792
|
-
yield*
|
|
13705
|
+
yield* Effect16.sync(
|
|
13793
13706
|
() => emitDigestStatus(
|
|
13794
13707
|
handlers,
|
|
13795
13708
|
result ? `Fetched ${String(result.count)} mentions from ${result.source}` : "Mention fetch failed; using local data"
|
|
@@ -13797,7 +13710,7 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13797
13710
|
);
|
|
13798
13711
|
}
|
|
13799
13712
|
if (includeThreads) {
|
|
13800
|
-
yield*
|
|
13713
|
+
yield* Effect16.sync(
|
|
13801
13714
|
() => emitDigestStatus(
|
|
13802
13715
|
handlers,
|
|
13803
13716
|
"Fetching mention conversations",
|
|
@@ -13818,23 +13731,23 @@ function refreshPeriodDigestInputsEffect(options, phase = {}, handlers = {}) {
|
|
|
13818
13731
|
`${String(progress.fetched)} tweets \xB7 ${progress.source}${progress.done ? " \xB7 done" : ""}`
|
|
13819
13732
|
)
|
|
13820
13733
|
}).pipe(
|
|
13821
|
-
|
|
13734
|
+
Effect16.match({
|
|
13822
13735
|
onFailure: () => null,
|
|
13823
13736
|
onSuccess: (value) => value
|
|
13824
13737
|
})
|
|
13825
13738
|
);
|
|
13826
|
-
yield*
|
|
13739
|
+
yield* Effect16.sync(
|
|
13827
13740
|
() => emitDigestStatus(
|
|
13828
13741
|
handlers,
|
|
13829
13742
|
result ? `Fetched ${String(result.uniqueTweets)} conversation tweets` : "Conversation fetch failed; using available context"
|
|
13830
13743
|
)
|
|
13831
13744
|
);
|
|
13832
13745
|
}
|
|
13833
|
-
yield*
|
|
13746
|
+
yield* Effect16.sync(
|
|
13834
13747
|
() => emitDigestStatus(handlers, "Preparing local AI context")
|
|
13835
13748
|
);
|
|
13836
|
-
yield* maybeAutoSyncBackupEffect().pipe(
|
|
13837
|
-
}).pipe(
|
|
13749
|
+
yield* maybeAutoSyncBackupEffect().pipe(Effect16.catchAll(() => Effect16.void));
|
|
13750
|
+
}).pipe(Effect16.asVoid);
|
|
13838
13751
|
}
|
|
13839
13752
|
function digestCacheKey(context, options) {
|
|
13840
13753
|
const parts = [
|
|
@@ -14043,7 +13956,7 @@ function createOpenAIRequestBody(context, options) {
|
|
|
14043
13956
|
});
|
|
14044
13957
|
}
|
|
14045
13958
|
function completeOpenAIStreamEffect(stream, context, options, handlers) {
|
|
14046
|
-
return
|
|
13959
|
+
return Effect16.gen(function* () {
|
|
14047
13960
|
const enrichedContext = yield* tryDigestSync(
|
|
14048
13961
|
() => enrichContextWithCitedTweets(context, stream.value)
|
|
14049
13962
|
);
|
|
@@ -14085,7 +13998,7 @@ function completeOpenAIStreamEffect(stream, context, options, handlers) {
|
|
|
14085
13998
|
});
|
|
14086
13999
|
}
|
|
14087
14000
|
function streamPeriodDigestEffect(options = {}, handlers = {}) {
|
|
14088
|
-
return
|
|
14001
|
+
return Effect16.gen(function* () {
|
|
14089
14002
|
const resolvedOptions = {
|
|
14090
14003
|
...options,
|
|
14091
14004
|
language: yield* tryDigestSync(() => languageFromOptions(options))
|
|
@@ -14107,7 +14020,7 @@ function streamPeriodDigestEffect(options = {}, handlers = {}) {
|
|
|
14107
14020
|
resolvedOptions,
|
|
14108
14021
|
{ threads: false },
|
|
14109
14022
|
handlers
|
|
14110
|
-
).pipe(
|
|
14023
|
+
).pipe(Effect16.catchAll(() => Effect16.void));
|
|
14111
14024
|
let context = yield* tryDigestSync(
|
|
14112
14025
|
() => collectPeriodDigestContext(resolvedOptions)
|
|
14113
14026
|
);
|
|
@@ -14142,7 +14055,7 @@ function streamPeriodDigestEffect(options = {}, handlers = {}) {
|
|
|
14142
14055
|
threadTweetIds: context.tweets.filter((tweet) => tweet.source === "mentions").map((tweet) => tweet.id)
|
|
14143
14056
|
},
|
|
14144
14057
|
handlers
|
|
14145
|
-
).pipe(
|
|
14058
|
+
).pipe(Effect16.catchAll(() => Effect16.void));
|
|
14146
14059
|
context = yield* tryDigestSync(
|
|
14147
14060
|
() => collectPeriodDigestContext(resolvedOptions)
|
|
14148
14061
|
);
|
|
@@ -14174,7 +14087,7 @@ function streamPeriodDigest(options = {}, handlers = {}) {
|
|
|
14174
14087
|
|
|
14175
14088
|
// src/lib/profile-analysis.ts
|
|
14176
14089
|
import { createHash as createHash6 } from "node:crypto";
|
|
14177
|
-
import { Effect as
|
|
14090
|
+
import { Effect as Effect17 } from "effect";
|
|
14178
14091
|
import { z as z2 } from "zod";
|
|
14179
14092
|
|
|
14180
14093
|
// src/lib/xurl-rate-limits.ts
|
|
@@ -14277,13 +14190,17 @@ function toError7(error) {
|
|
|
14277
14190
|
return error instanceof Error ? error : new Error(String(error));
|
|
14278
14191
|
}
|
|
14279
14192
|
function isXurlRateLimitError(error) {
|
|
14193
|
+
const tagged = error;
|
|
14194
|
+
if (tagged._tag === "XurlCommandError" && tagged.rateLimited === true) {
|
|
14195
|
+
return true;
|
|
14196
|
+
}
|
|
14280
14197
|
return error.message.includes("Too Many Requests") || error.message.includes('"status":429') || /\b429\b/.test(error.message);
|
|
14281
14198
|
}
|
|
14282
14199
|
function tryProfileSync(try_) {
|
|
14283
|
-
return
|
|
14200
|
+
return Effect17.try({ try: try_, catch: toError7 });
|
|
14284
14201
|
}
|
|
14285
14202
|
function tryProfilePromise(try_) {
|
|
14286
|
-
return tryPromise(try_).pipe(
|
|
14203
|
+
return tryPromise(try_).pipe(Effect17.mapError(toError7));
|
|
14287
14204
|
}
|
|
14288
14205
|
function normalizeHandle(value) {
|
|
14289
14206
|
const handle = value.trim().replace(/^https?:\/\/(x|twitter)\.com\//i, "").replace(/^@/, "").split(/[/?#]/)[0]?.trim();
|
|
@@ -14668,7 +14585,7 @@ function sleepWithAbortEffect(ms, signal) {
|
|
|
14668
14585
|
);
|
|
14669
14586
|
}
|
|
14670
14587
|
function collectProfileAnalysisContextEffect(options, handlers = {}) {
|
|
14671
|
-
return
|
|
14588
|
+
return Effect17.gen(function* () {
|
|
14672
14589
|
const db = getNativeDb();
|
|
14673
14590
|
const handle = yield* tryProfileSync(() => normalizeHandle(options.handle));
|
|
14674
14591
|
const account = yield* tryProfileSync(
|
|
@@ -14745,7 +14662,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
|
|
|
14745
14662
|
});
|
|
14746
14663
|
yield* abortIfRequestedEffect(options.signal);
|
|
14747
14664
|
if (!user) {
|
|
14748
|
-
return yield*
|
|
14665
|
+
return yield* Effect17.fail(new Error(`Could not resolve @${handle}`));
|
|
14749
14666
|
}
|
|
14750
14667
|
const resolved = yield* tryProfileSync(
|
|
14751
14668
|
() => upsertProfileFromXUser(db, user)
|
|
@@ -14849,9 +14766,9 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
|
|
|
14849
14766
|
signal: options.signal,
|
|
14850
14767
|
onAttempt: recordConversationAttempt
|
|
14851
14768
|
}).pipe(
|
|
14852
|
-
|
|
14769
|
+
Effect17.catchAll((error) => {
|
|
14853
14770
|
if (!isXurlRateLimitError(error)) {
|
|
14854
|
-
return
|
|
14771
|
+
return Effect17.fail(error);
|
|
14855
14772
|
}
|
|
14856
14773
|
if (attempt < rateLimitMaxRetries) {
|
|
14857
14774
|
emitStatus(
|
|
@@ -14862,7 +14779,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
|
|
|
14862
14779
|
return sleepWithAbortEffect(
|
|
14863
14780
|
rateLimitRetryMs,
|
|
14864
14781
|
options.signal
|
|
14865
|
-
).pipe(
|
|
14782
|
+
).pipe(Effect17.as(null));
|
|
14866
14783
|
}
|
|
14867
14784
|
conversationRateLimited = true;
|
|
14868
14785
|
emitStatus(
|
|
@@ -14870,7 +14787,7 @@ function collectProfileAnalysisContextEffect(options, handlers = {}) {
|
|
|
14870
14787
|
"Conversation fetch rate limited",
|
|
14871
14788
|
"using partial profile context"
|
|
14872
14789
|
);
|
|
14873
|
-
return
|
|
14790
|
+
return Effect17.succeed(null);
|
|
14874
14791
|
})
|
|
14875
14792
|
);
|
|
14876
14793
|
if (response || conversationRateLimited) {
|
|
@@ -15016,7 +14933,7 @@ function createOpenAIRequestBody2(context, options) {
|
|
|
15016
14933
|
});
|
|
15017
14934
|
}
|
|
15018
14935
|
function streamProfileAnalysisEffect(options, handlers = {}) {
|
|
15019
|
-
return
|
|
14936
|
+
return Effect17.gen(function* () {
|
|
15020
14937
|
const context = yield* collectProfileAnalysisContextEffect(
|
|
15021
14938
|
options,
|
|
15022
14939
|
handlers
|
|
@@ -15082,10 +14999,10 @@ function streamProfileAnalysis(options, handlers = {}) {
|
|
|
15082
14999
|
// src/lib/research.ts
|
|
15083
15000
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
15084
15001
|
import path4 from "node:path";
|
|
15085
|
-
import { Effect as
|
|
15002
|
+
import { Effect as Effect19 } from "effect";
|
|
15086
15003
|
|
|
15087
15004
|
// src/lib/tweet-lookup.ts
|
|
15088
|
-
import { Effect as
|
|
15005
|
+
import { Effect as Effect18 } from "effect";
|
|
15089
15006
|
function errorMessage2(error) {
|
|
15090
15007
|
return error instanceof Error ? error.message : String(error);
|
|
15091
15008
|
}
|
|
@@ -15097,10 +15014,10 @@ function lookupTweetsByIdsEffect2(ids, mode = "auto") {
|
|
|
15097
15014
|
return lookupTweetsByIdsEffect(ids);
|
|
15098
15015
|
}
|
|
15099
15016
|
return lookupTweetsByIdsEffect(ids).pipe(
|
|
15100
|
-
|
|
15017
|
+
Effect18.catchAll(
|
|
15101
15018
|
(xurlError) => lookupTweetsByIdsViaBirdEffect(ids).pipe(
|
|
15102
|
-
|
|
15103
|
-
(birdError) =>
|
|
15019
|
+
Effect18.catchAll(
|
|
15020
|
+
(birdError) => Effect18.fail(
|
|
15104
15021
|
new Error(
|
|
15105
15022
|
`Tweet lookup failed via xurl and bird: xurl: ${errorMessage2(
|
|
15106
15023
|
xurlError
|
|
@@ -15115,7 +15032,7 @@ function lookupTweetsByIdsEffect2(ids, mode = "auto") {
|
|
|
15115
15032
|
|
|
15116
15033
|
// src/lib/research.ts
|
|
15117
15034
|
function trySync4(try_) {
|
|
15118
|
-
return
|
|
15035
|
+
return Effect19.try({
|
|
15119
15036
|
try: try_,
|
|
15120
15037
|
catch: (cause) => cause
|
|
15121
15038
|
});
|
|
@@ -15299,7 +15216,7 @@ function getTweetDescendants(rootTweetId, maxThreadDepth) {
|
|
|
15299
15216
|
return rows.map((row) => toResearchNode(row, "local"));
|
|
15300
15217
|
}
|
|
15301
15218
|
function lookupTweetNodeEffect(tweetId) {
|
|
15302
|
-
return
|
|
15219
|
+
return Effect19.gen(function* () {
|
|
15303
15220
|
const payload = yield* lookupTweetsByIdsEffect2([tweetId]);
|
|
15304
15221
|
const tweet = payload.data[0];
|
|
15305
15222
|
if (!tweet) {
|
|
@@ -15337,7 +15254,7 @@ function lookupTweetNodeEffect(tweetId) {
|
|
|
15337
15254
|
});
|
|
15338
15255
|
}
|
|
15339
15256
|
function collectAncestorChainEffect(tweetId, maxThreadDepth) {
|
|
15340
|
-
return
|
|
15257
|
+
return Effect19.gen(function* () {
|
|
15341
15258
|
const chain = [];
|
|
15342
15259
|
let currentId = tweetId;
|
|
15343
15260
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -15504,7 +15421,7 @@ function resolveSeedTimelineItems({
|
|
|
15504
15421
|
});
|
|
15505
15422
|
}
|
|
15506
15423
|
function runResearchModeEffect(options = {}) {
|
|
15507
|
-
return
|
|
15424
|
+
return Effect19.gen(function* () {
|
|
15508
15425
|
const limit = yield* trySync4(
|
|
15509
15426
|
() => Number.isFinite(options.limit ?? 20) ? Math.max(1, Math.floor(options.limit ?? 20)) : 20
|
|
15510
15427
|
);
|
|
@@ -15577,11 +15494,11 @@ function runResearchMode(options = {}) {
|
|
|
15577
15494
|
|
|
15578
15495
|
// src/lib/search-discussion.ts
|
|
15579
15496
|
import { createHash as createHash7 } from "node:crypto";
|
|
15580
|
-
import { Effect as
|
|
15497
|
+
import { Effect as Effect21 } from "effect";
|
|
15581
15498
|
import { z as z3 } from "zod";
|
|
15582
15499
|
|
|
15583
15500
|
// src/lib/tweet-search-live.ts
|
|
15584
|
-
import { Effect as
|
|
15501
|
+
import { Effect as Effect20 } from "effect";
|
|
15585
15502
|
var DEFAULT_SEARCH_LIMIT = 2e4;
|
|
15586
15503
|
var DEFAULT_MAX_PAGES2 = 200;
|
|
15587
15504
|
var DEFAULT_CACHE_TTL_MS2 = 2 * 6e4;
|
|
@@ -15698,7 +15615,7 @@ function fetchXurlSearchEffect({
|
|
|
15698
15615
|
since,
|
|
15699
15616
|
until
|
|
15700
15617
|
}) {
|
|
15701
|
-
return
|
|
15618
|
+
return Effect20.gen(function* () {
|
|
15702
15619
|
const result = yield* runSyncPlanEffect({
|
|
15703
15620
|
fetchPage: ({ cursor, fetched }) => {
|
|
15704
15621
|
const remaining = Math.max(1, limit - fetched);
|
|
@@ -15708,7 +15625,7 @@ function fetchXurlSearchEffect({
|
|
|
15708
15625
|
startTime: since,
|
|
15709
15626
|
endTime: until,
|
|
15710
15627
|
timeoutMs
|
|
15711
|
-
}).pipe(
|
|
15628
|
+
}).pipe(Effect20.map(toMentionsResponse));
|
|
15712
15629
|
},
|
|
15713
15630
|
getItemCount: (page) => page.data.length,
|
|
15714
15631
|
getNextCursor: (page) => typeof page.meta?.next_token === "string" ? page.meta.next_token : void 0,
|
|
@@ -15719,10 +15636,10 @@ function fetchXurlSearchEffect({
|
|
|
15719
15636
|
});
|
|
15720
15637
|
}
|
|
15721
15638
|
function runModeEffect(mode, options) {
|
|
15722
|
-
return
|
|
15639
|
+
return Effect20.gen(function* () {
|
|
15723
15640
|
const db = getNativeDb();
|
|
15724
15641
|
const key = cacheKey({ ...options, mode });
|
|
15725
|
-
const fetch2 = mode === "bird" ? fetchBirdSearchEffect(options).pipe(
|
|
15642
|
+
const fetch2 = mode === "bird" ? fetchBirdSearchEffect(options).pipe(Effect20.mapError(toError)) : fetchXurlSearchEffect(options);
|
|
15726
15643
|
const syncResult = yield* runCachedLiveSyncEffect({
|
|
15727
15644
|
db,
|
|
15728
15645
|
cacheKey: key,
|
|
@@ -15732,7 +15649,7 @@ function runModeEffect(mode, options) {
|
|
|
15732
15649
|
{
|
|
15733
15650
|
source: mode,
|
|
15734
15651
|
fetch: fetch2.pipe(
|
|
15735
|
-
|
|
15652
|
+
Effect20.map((response) => limitResponse(response, options.limit))
|
|
15736
15653
|
)
|
|
15737
15654
|
}
|
|
15738
15655
|
],
|
|
@@ -15803,10 +15720,10 @@ function syncTweetSearchEffect({
|
|
|
15803
15720
|
cacheTtlMs,
|
|
15804
15721
|
timeoutMs
|
|
15805
15722
|
}) {
|
|
15806
|
-
return
|
|
15723
|
+
return Effect20.gen(function* () {
|
|
15807
15724
|
const normalizedQuery = query.trim();
|
|
15808
15725
|
if (!normalizedQuery) {
|
|
15809
|
-
return yield*
|
|
15726
|
+
return yield* Effect20.fail(new Error("Search query is required"));
|
|
15810
15727
|
}
|
|
15811
15728
|
const normalizedLimit = normalizeLimit(limit);
|
|
15812
15729
|
const normalizedMaxPages = normalizeMaxPages(maxPages);
|
|
@@ -15847,8 +15764,8 @@ function syncTweetSearchEffect({
|
|
|
15847
15764
|
};
|
|
15848
15765
|
if (mode === "bird" || mode === "xurl") {
|
|
15849
15766
|
return yield* runModeEffect(mode, runOptions).pipe(
|
|
15850
|
-
|
|
15851
|
-
(error) =>
|
|
15767
|
+
Effect20.catchAll(
|
|
15768
|
+
(error) => Effect20.succeed({
|
|
15852
15769
|
ok: false,
|
|
15853
15770
|
source: mode,
|
|
15854
15771
|
accountId,
|
|
@@ -15860,8 +15777,8 @@ function syncTweetSearchEffect({
|
|
|
15860
15777
|
}
|
|
15861
15778
|
if (normalizedSince || normalizedUntil) {
|
|
15862
15779
|
return yield* runModeEffect("xurl", runOptions).pipe(
|
|
15863
|
-
|
|
15864
|
-
(error) =>
|
|
15780
|
+
Effect20.catchAll(
|
|
15781
|
+
(error) => Effect20.succeed({
|
|
15865
15782
|
ok: false,
|
|
15866
15783
|
source: "auto",
|
|
15867
15784
|
accountId,
|
|
@@ -15872,8 +15789,8 @@ function syncTweetSearchEffect({
|
|
|
15872
15789
|
);
|
|
15873
15790
|
}
|
|
15874
15791
|
const birdResult = yield* runModeEffect("bird", runOptions).pipe(
|
|
15875
|
-
|
|
15876
|
-
(error) =>
|
|
15792
|
+
Effect20.catchAll(
|
|
15793
|
+
(error) => Effect20.succeed({
|
|
15877
15794
|
ok: false,
|
|
15878
15795
|
source: "bird",
|
|
15879
15796
|
accountId,
|
|
@@ -15883,8 +15800,8 @@ function syncTweetSearchEffect({
|
|
|
15883
15800
|
)
|
|
15884
15801
|
);
|
|
15885
15802
|
const xurlResult = yield* runModeEffect("xurl", runOptions).pipe(
|
|
15886
|
-
|
|
15887
|
-
(error) =>
|
|
15803
|
+
Effect20.catchAll(
|
|
15804
|
+
(error) => Effect20.succeed({
|
|
15888
15805
|
ok: false,
|
|
15889
15806
|
source: "xurl",
|
|
15890
15807
|
accountId,
|
|
@@ -15923,7 +15840,7 @@ function toError8(error) {
|
|
|
15923
15840
|
return error instanceof Error ? error : new Error(String(error));
|
|
15924
15841
|
}
|
|
15925
15842
|
function trySearchSync(try_) {
|
|
15926
|
-
return
|
|
15843
|
+
return Effect21.try({
|
|
15927
15844
|
try: try_,
|
|
15928
15845
|
catch: toError8
|
|
15929
15846
|
});
|
|
@@ -16214,8 +16131,8 @@ function prefetchDiscussionAvatars(context) {
|
|
|
16214
16131
|
}
|
|
16215
16132
|
runEffectBackground(
|
|
16216
16133
|
prefetchCachedAvatarsForProfileIdsEffect(profileIds).pipe(
|
|
16217
|
-
|
|
16218
|
-
() =>
|
|
16134
|
+
Effect21.catchAll(
|
|
16135
|
+
() => Effect21.succeed({
|
|
16219
16136
|
requested: 0,
|
|
16220
16137
|
available: 0,
|
|
16221
16138
|
missing: 0,
|
|
@@ -16351,7 +16268,7 @@ function createOpenAIRequestBody3(context, options) {
|
|
|
16351
16268
|
});
|
|
16352
16269
|
}
|
|
16353
16270
|
function completeOpenAIStreamEffect2(stream, context, options, handlers) {
|
|
16354
|
-
return
|
|
16271
|
+
return Effect21.gen(function* () {
|
|
16355
16272
|
const updatedAt = yield* trySearchSync(
|
|
16356
16273
|
() => writeSyncCache(cacheKey2(context, options), {
|
|
16357
16274
|
discussion: stream.value,
|
|
@@ -16378,7 +16295,7 @@ function completeOpenAIStreamEffect2(stream, context, options, handlers) {
|
|
|
16378
16295
|
});
|
|
16379
16296
|
}
|
|
16380
16297
|
function streamSearchDiscussionEffect(options, handlers = {}) {
|
|
16381
|
-
return
|
|
16298
|
+
return Effect21.gen(function* () {
|
|
16382
16299
|
const mode = options.mode ?? "auto";
|
|
16383
16300
|
const liveSearch = mode === "local" ? void 0 : yield* syncTweetSearchEffect({
|
|
16384
16301
|
query: options.query,
|
|
@@ -16392,7 +16309,7 @@ function streamSearchDiscussionEffect(options, handlers = {}) {
|
|
|
16392
16309
|
timeoutMs: 3e4
|
|
16393
16310
|
});
|
|
16394
16311
|
if (liveSearch && !liveSearch.ok) {
|
|
16395
|
-
return yield*
|
|
16312
|
+
return yield* Effect21.fail(
|
|
16396
16313
|
new Error(
|
|
16397
16314
|
`Live tweet search failed via ${liveSearch.source}: ${liveSearch.error}`
|
|
16398
16315
|
)
|
|
@@ -16772,12 +16689,12 @@ function registerAnalysisCommands({
|
|
|
16772
16689
|
|
|
16773
16690
|
// src/lib/query-actions.ts
|
|
16774
16691
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16775
|
-
import { Effect as
|
|
16692
|
+
import { Effect as Effect22 } from "effect";
|
|
16776
16693
|
function toError9(error) {
|
|
16777
16694
|
return error instanceof Error ? error : new Error(String(error));
|
|
16778
16695
|
}
|
|
16779
16696
|
function trySync5(try_) {
|
|
16780
|
-
return
|
|
16697
|
+
return Effect22.try({ try: try_, catch: toError9 });
|
|
16781
16698
|
}
|
|
16782
16699
|
function e2eFakeLiveWritesEnabled2() {
|
|
16783
16700
|
return process.env.BIRDCLAW_E2E === "1" && process.env.BIRDCLAW_E2E_FAKE_LIVE_WRITES === "1";
|
|
@@ -16786,7 +16703,7 @@ function liveWritesDisabled2() {
|
|
|
16786
16703
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
16787
16704
|
}
|
|
16788
16705
|
function verifySelectedXurlAccountEffect(accountId) {
|
|
16789
|
-
return
|
|
16706
|
+
return Effect22.gen(function* () {
|
|
16790
16707
|
if (liveWritesDisabled2()) return;
|
|
16791
16708
|
if (e2eFakeLiveWritesEnabled2()) return;
|
|
16792
16709
|
const db = yield* trySync5(() => getReadDb());
|
|
@@ -16794,7 +16711,7 @@ function verifySelectedXurlAccountEffect(accountId) {
|
|
|
16794
16711
|
() => db.prepare("select handle, external_user_id from accounts where id = ?").get(accountId)
|
|
16795
16712
|
);
|
|
16796
16713
|
if (!account) {
|
|
16797
|
-
return yield*
|
|
16714
|
+
return yield* Effect22.fail(new Error(`Unknown account: ${accountId}`));
|
|
16798
16715
|
}
|
|
16799
16716
|
const authenticated = yield* tryPromise(
|
|
16800
16717
|
() => lookupAuthenticatedUserFresh()
|
|
@@ -16803,7 +16720,7 @@ function verifySelectedXurlAccountEffect(accountId) {
|
|
|
16803
16720
|
const authenticatedHandle = typeof authenticated?.username === "string" ? authenticated.username.replace(/^@/, "") : "";
|
|
16804
16721
|
const expectedHandle = account.handle.replace(/^@/, "");
|
|
16805
16722
|
if (account.external_user_id && account.external_user_id !== authenticatedId || !account.external_user_id && (!authenticatedHandle || authenticatedHandle.toLowerCase() !== expectedHandle.toLowerCase())) {
|
|
16806
|
-
return yield*
|
|
16723
|
+
return yield* Effect22.fail(
|
|
16807
16724
|
new Error(
|
|
16808
16725
|
`xurl is authenticated as @${authenticatedHandle || authenticatedId}, not @${expectedHandle}`
|
|
16809
16726
|
)
|
|
@@ -16915,7 +16832,7 @@ function writePostDraft(db, accountId, text, draft) {
|
|
|
16915
16832
|
);
|
|
16916
16833
|
}
|
|
16917
16834
|
function createPostEffect(accountId, text) {
|
|
16918
|
-
return
|
|
16835
|
+
return Effect22.gen(function* () {
|
|
16919
16836
|
const draft = yield* trySync5(() => preparePostDraft(accountId));
|
|
16920
16837
|
yield* databaseWriteEffect(
|
|
16921
16838
|
(db) => preflightWrite(
|
|
@@ -16926,7 +16843,7 @@ function createPostEffect(accountId, text) {
|
|
|
16926
16843
|
yield* verifySelectedXurlAccountEffect(accountId);
|
|
16927
16844
|
const transport = yield* postViaXurlEffect(text);
|
|
16928
16845
|
if (!transport.ok) {
|
|
16929
|
-
return yield*
|
|
16846
|
+
return yield* Effect22.fail(new Error(transport.output || "post failed"));
|
|
16930
16847
|
}
|
|
16931
16848
|
yield* databaseWriteEffect(
|
|
16932
16849
|
(db) => writePostDraft(db, accountId, text, draft)
|
|
@@ -16970,7 +16887,7 @@ function createTweetReplyEffect(accountId, tweetId, text) {
|
|
|
16970
16887
|
"insert into tweet_actions (id, account_id, tweet_id, kind, body, created_at) values (?, ?, ?, ?, ?, ?)"
|
|
16971
16888
|
).run(draft.actionId, accountId, tweetId, "reply", text, draft.createdAt);
|
|
16972
16889
|
}
|
|
16973
|
-
return
|
|
16890
|
+
return Effect22.gen(function* () {
|
|
16974
16891
|
const draft = yield* trySync5(() => prepareReplyDraft());
|
|
16975
16892
|
yield* databaseWriteEffect(
|
|
16976
16893
|
(db) => preflightWrite(db, (writeDb) => writeReplyDraft(writeDb, draft))
|
|
@@ -16978,7 +16895,7 @@ function createTweetReplyEffect(accountId, tweetId, text) {
|
|
|
16978
16895
|
yield* verifySelectedXurlAccountEffect(accountId);
|
|
16979
16896
|
const transport = yield* replyViaXurlEffect(tweetId, text);
|
|
16980
16897
|
if (!transport.ok) {
|
|
16981
|
-
return yield*
|
|
16898
|
+
return yield* Effect22.fail(new Error(transport.output || "reply failed"));
|
|
16982
16899
|
}
|
|
16983
16900
|
yield* databaseWriteEffect((db) => writeReplyDraft(db, draft));
|
|
16984
16901
|
return { ok: true, transport, replyId: draft.replyId };
|
|
@@ -16988,7 +16905,7 @@ function createTweetReply(accountId, tweetId, text) {
|
|
|
16988
16905
|
return runEffectPromise(createTweetReplyEffect(accountId, tweetId, text));
|
|
16989
16906
|
}
|
|
16990
16907
|
function createDmReplyEffect(conversationId, text) {
|
|
16991
|
-
return
|
|
16908
|
+
return Effect22.gen(function* () {
|
|
16992
16909
|
const draft = yield* trySync5(() => {
|
|
16993
16910
|
const conversation = getConversationThread(conversationId);
|
|
16994
16911
|
if (!conversation) {
|
|
@@ -17037,7 +16954,7 @@ function createDmReplyEffect(conversationId, text) {
|
|
|
17037
16954
|
yield* verifySelectedXurlAccountEffect(draft.accountId);
|
|
17038
16955
|
const transport = yield* dmViaXurlEffect(draft.handle, text);
|
|
17039
16956
|
if (!transport.ok) {
|
|
17040
|
-
return yield*
|
|
16957
|
+
return yield* Effect22.fail(new Error(transport.output || "dm failed"));
|
|
17041
16958
|
}
|
|
17042
16959
|
yield* databaseWriteEffect((db) => {
|
|
17043
16960
|
db.prepare(
|
|
@@ -17111,7 +17028,7 @@ async function applyDmRequestMutationToLocalStore(conversationId, action) {
|
|
|
17111
17028
|
}
|
|
17112
17029
|
|
|
17113
17030
|
// src/lib/query-status.ts
|
|
17114
|
-
import { Effect as
|
|
17031
|
+
import { Effect as Effect24 } from "effect";
|
|
17115
17032
|
|
|
17116
17033
|
// src/lib/archive-finder.ts
|
|
17117
17034
|
import { exec } from "node:child_process";
|
|
@@ -17119,7 +17036,7 @@ import { existsSync as existsSync3, promises as fs2 } from "node:fs";
|
|
|
17119
17036
|
import { homedir } from "node:os";
|
|
17120
17037
|
import path5 from "node:path";
|
|
17121
17038
|
import { promisify as promisify3 } from "node:util";
|
|
17122
|
-
import { Effect as
|
|
17039
|
+
import { Effect as Effect23 } from "effect";
|
|
17123
17040
|
var execAsync = promisify3(exec);
|
|
17124
17041
|
var ARCHIVE_DISCOVERY_CACHE_MS = 5 * 6e4;
|
|
17125
17042
|
var ARCHIVE_NAME_PATTERNS = [
|
|
@@ -17151,7 +17068,7 @@ function formatRelativeDate(date) {
|
|
|
17151
17068
|
return `${Math.floor(days / 365)} years ago`;
|
|
17152
17069
|
}
|
|
17153
17070
|
function getCandidateEffect(filePath) {
|
|
17154
|
-
return
|
|
17071
|
+
return Effect23.gen(function* () {
|
|
17155
17072
|
const stats = yield* tryPromise(() => fs2.stat(filePath));
|
|
17156
17073
|
if (!stats.isFile() || stats.size < 1024 * 1024) {
|
|
17157
17074
|
return null;
|
|
@@ -17164,43 +17081,43 @@ function getCandidateEffect(filePath) {
|
|
|
17164
17081
|
modifiedTime: stats.mtime.toISOString(),
|
|
17165
17082
|
dateFormatted: formatRelativeDate(stats.mtime)
|
|
17166
17083
|
};
|
|
17167
|
-
}).pipe(
|
|
17084
|
+
}).pipe(Effect23.catchAll(() => Effect23.succeed(null)));
|
|
17168
17085
|
}
|
|
17169
17086
|
function searchDirectoryEffect(directoryPath) {
|
|
17170
17087
|
if (!existsSync3(directoryPath)) {
|
|
17171
|
-
return
|
|
17088
|
+
return Effect23.succeed([]);
|
|
17172
17089
|
}
|
|
17173
|
-
return
|
|
17090
|
+
return Effect23.gen(function* () {
|
|
17174
17091
|
const entries = yield* tryPromise(() => fs2.readdir(directoryPath));
|
|
17175
17092
|
const matches = entries.filter(
|
|
17176
17093
|
(entry) => ARCHIVE_NAME_PATTERNS.some((pattern) => pattern.test(entry))
|
|
17177
17094
|
);
|
|
17178
|
-
const candidates = yield*
|
|
17095
|
+
const candidates = yield* Effect23.forEach(
|
|
17179
17096
|
matches,
|
|
17180
17097
|
(entry) => getCandidateEffect(path5.join(directoryPath, entry)),
|
|
17181
17098
|
{ concurrency: "unbounded" }
|
|
17182
17099
|
);
|
|
17183
17100
|
return candidates.filter((item) => item !== null);
|
|
17184
|
-
}).pipe(
|
|
17101
|
+
}).pipe(Effect23.catchAll(() => Effect23.succeed([])));
|
|
17185
17102
|
}
|
|
17186
17103
|
function searchSpotlightEffect(query) {
|
|
17187
|
-
return
|
|
17104
|
+
return Effect23.gen(function* () {
|
|
17188
17105
|
const { stdout } = yield* tryPromise(
|
|
17189
17106
|
() => execAsync(`mdfind -onlyin ~ '${query}'`, {
|
|
17190
17107
|
timeout: 5e3
|
|
17191
17108
|
})
|
|
17192
17109
|
);
|
|
17193
17110
|
const paths = stdout.split("\n").map((item) => item.trim()).filter((item) => item.length > 0 && item.endsWith(".zip"));
|
|
17194
|
-
return yield*
|
|
17111
|
+
return yield* Effect23.forEach(paths, getCandidateEffect, {
|
|
17195
17112
|
concurrency: "unbounded"
|
|
17196
17113
|
});
|
|
17197
|
-
}).pipe(
|
|
17114
|
+
}).pipe(Effect23.catchAll(() => Effect23.succeed([])));
|
|
17198
17115
|
}
|
|
17199
17116
|
function findArchivesEffect() {
|
|
17200
17117
|
if (process.platform !== "darwin") {
|
|
17201
|
-
return
|
|
17118
|
+
return Effect23.succeed([]);
|
|
17202
17119
|
}
|
|
17203
|
-
return
|
|
17120
|
+
return Effect23.gen(function* () {
|
|
17204
17121
|
const found = /* @__PURE__ */ new Map();
|
|
17205
17122
|
const downloads = yield* searchDirectoryEffect(
|
|
17206
17123
|
path5.join(homedir(), "Downloads")
|
|
@@ -17213,7 +17130,7 @@ function findArchivesEffect() {
|
|
|
17213
17130
|
'kMDItemDisplayName == "x-*.zip"',
|
|
17214
17131
|
'kMDItemDisplayName == "*archive*.zip" && kMDItemKind == "Zip archive"'
|
|
17215
17132
|
];
|
|
17216
|
-
const spotlightCandidates = yield*
|
|
17133
|
+
const spotlightCandidates = yield* Effect23.forEach(
|
|
17217
17134
|
queries,
|
|
17218
17135
|
searchSpotlightEffect,
|
|
17219
17136
|
{ concurrency: "unbounded" }
|
|
@@ -17231,13 +17148,13 @@ function findArchivesEffect() {
|
|
|
17231
17148
|
});
|
|
17232
17149
|
}
|
|
17233
17150
|
function findArchivesCachedEffect() {
|
|
17234
|
-
return
|
|
17151
|
+
return Effect23.suspend(() => {
|
|
17235
17152
|
const cached = archiveDiscoveryCache;
|
|
17236
17153
|
if (cached && Date.now() - cached.updatedAt < ARCHIVE_DISCOVERY_CACHE_MS) {
|
|
17237
|
-
return
|
|
17154
|
+
return Effect23.succeed(cached.value);
|
|
17238
17155
|
}
|
|
17239
17156
|
if (archiveDiscoveryInFlight) {
|
|
17240
|
-
return
|
|
17157
|
+
return Effect23.promise(() => archiveDiscoveryInFlight);
|
|
17241
17158
|
}
|
|
17242
17159
|
const request = runEffectPromise(findArchivesEffect()).then((value) => {
|
|
17243
17160
|
archiveDiscoveryCache = { value, updatedAt: Date.now() };
|
|
@@ -17248,7 +17165,7 @@ function findArchivesCachedEffect() {
|
|
|
17248
17165
|
}
|
|
17249
17166
|
});
|
|
17250
17167
|
archiveDiscoveryInFlight = request;
|
|
17251
|
-
return
|
|
17168
|
+
return Effect23.promise(() => request);
|
|
17252
17169
|
});
|
|
17253
17170
|
}
|
|
17254
17171
|
function findArchives() {
|
|
@@ -17260,7 +17177,7 @@ function toError10(error) {
|
|
|
17260
17177
|
return error instanceof Error ? error : new Error(String(error));
|
|
17261
17178
|
}
|
|
17262
17179
|
function trySync6(try_) {
|
|
17263
|
-
return
|
|
17180
|
+
return Effect24.try({ try: try_, catch: toError10 });
|
|
17264
17181
|
}
|
|
17265
17182
|
function countTimelineEdges(db, kind) {
|
|
17266
17183
|
const row = db.prepare(
|
|
@@ -17294,7 +17211,7 @@ function getAccountProfileMeta(db, account) {
|
|
|
17294
17211
|
function getQueryEnvelopeEffect({
|
|
17295
17212
|
includeArchives = true
|
|
17296
17213
|
} = {}) {
|
|
17297
|
-
return
|
|
17214
|
+
return Effect24.gen(function* () {
|
|
17298
17215
|
const nativeDb2 = yield* trySync6(() => getReadDb());
|
|
17299
17216
|
const homeCount = yield* trySync6(
|
|
17300
17217
|
() => countTimelineEdges(nativeDb2, "home")
|
|
@@ -17302,7 +17219,7 @@ function getQueryEnvelopeEffect({
|
|
|
17302
17219
|
const mentionCount = yield* trySync6(
|
|
17303
17220
|
() => countTimelineEdges(nativeDb2, "mention")
|
|
17304
17221
|
);
|
|
17305
|
-
const counts = yield*
|
|
17222
|
+
const counts = yield* Effect24.all({
|
|
17306
17223
|
dms: trySync6(
|
|
17307
17224
|
() => nativeDb2.prepare("select count(*) as count from dm_conversations").get()
|
|
17308
17225
|
),
|
|
@@ -17316,7 +17233,7 @@ function getQueryEnvelopeEffect({
|
|
|
17316
17233
|
"select * from accounts order by is_default desc, name asc"
|
|
17317
17234
|
).all()
|
|
17318
17235
|
),
|
|
17319
|
-
archives: includeArchives ? findArchivesCachedEffect() :
|
|
17236
|
+
archives: includeArchives ? findArchivesCachedEffect() : Effect24.succeed([]),
|
|
17320
17237
|
transport: getTransportStatusEffect()
|
|
17321
17238
|
});
|
|
17322
17239
|
return {
|
|
@@ -17379,7 +17296,7 @@ function registerComposeCommands({
|
|
|
17379
17296
|
}
|
|
17380
17297
|
|
|
17381
17298
|
// src/lib/archive-import.ts
|
|
17382
|
-
import { Effect as
|
|
17299
|
+
import { Effect as Effect32 } from "effect";
|
|
17383
17300
|
|
|
17384
17301
|
// src/lib/archive-import-plan.ts
|
|
17385
17302
|
var ArchiveImportPlan = class {
|
|
@@ -17413,7 +17330,7 @@ var ArchiveImportPlan = class {
|
|
|
17413
17330
|
|
|
17414
17331
|
// src/lib/archive/apply.ts
|
|
17415
17332
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
17416
|
-
import { Effect as
|
|
17333
|
+
import { Effect as Effect25 } from "effect";
|
|
17417
17334
|
function applyArchiveImportPlanEffect({
|
|
17418
17335
|
archivePath,
|
|
17419
17336
|
db,
|
|
@@ -17443,7 +17360,7 @@ function applyArchiveImportPlanEffect({
|
|
|
17443
17360
|
followers: followerRows,
|
|
17444
17361
|
following: followingRows
|
|
17445
17362
|
} = plan;
|
|
17446
|
-
return
|
|
17363
|
+
return Effect25.gen(function* () {
|
|
17447
17364
|
const insertAccount = db.prepare(`
|
|
17448
17365
|
insert into accounts (id, name, handle, external_user_id, transport, is_default, created_at)
|
|
17449
17366
|
values (?, ?, ?, ?, ?, 1, ?)
|
|
@@ -18028,7 +17945,7 @@ function applyArchiveImportPlanEffect({
|
|
|
18028
17945
|
}
|
|
18029
17946
|
|
|
18030
17947
|
// src/lib/archive/collection-slice.ts
|
|
18031
|
-
import { Effect as
|
|
17948
|
+
import { Effect as Effect27 } from "effect";
|
|
18032
17949
|
|
|
18033
17950
|
// src/lib/archive/parsing.ts
|
|
18034
17951
|
function parseTwitterDate(value) {
|
|
@@ -18220,7 +18137,7 @@ function getArchiveFollowRow(wrapper, key) {
|
|
|
18220
18137
|
|
|
18221
18138
|
// src/lib/archive/reader.ts
|
|
18222
18139
|
import { spawn } from "node:child_process";
|
|
18223
|
-
import { Effect as
|
|
18140
|
+
import { Effect as Effect26 } from "effect";
|
|
18224
18141
|
var ARCHIVE_JSON_PAYLOAD = /=\s*(\[[\s\S]*\]|\{[\s\S]*\})/s;
|
|
18225
18142
|
function normalizeArchivePath(value) {
|
|
18226
18143
|
return value.replaceAll("\\", "/");
|
|
@@ -18238,10 +18155,10 @@ function runUnzipEffect(args, maxBuffer = 1024 * 1024 * 256) {
|
|
|
18238
18155
|
command: "unzip",
|
|
18239
18156
|
args,
|
|
18240
18157
|
maxBufferBytes: maxBuffer
|
|
18241
|
-
}).pipe(
|
|
18158
|
+
}).pipe(Effect26.map(({ stdout }) => stdout));
|
|
18242
18159
|
}
|
|
18243
18160
|
function listArchiveEntriesEffect(archivePath) {
|
|
18244
|
-
return
|
|
18161
|
+
return Effect26.gen(function* () {
|
|
18245
18162
|
const stdout = yield* runUnzipEffect(
|
|
18246
18163
|
["-Z1", archivePath],
|
|
18247
18164
|
1024 * 1024 * 64
|
|
@@ -18250,7 +18167,7 @@ function listArchiveEntriesEffect(archivePath) {
|
|
|
18250
18167
|
});
|
|
18251
18168
|
}
|
|
18252
18169
|
function listArchiveEntryDetailsEffect(archivePath) {
|
|
18253
|
-
return
|
|
18170
|
+
return Effect26.gen(function* () {
|
|
18254
18171
|
const stdout = yield* runUnzipEffect(
|
|
18255
18172
|
["-Z", "-l", archivePath],
|
|
18256
18173
|
1024 * 1024 * 64
|
|
@@ -18317,7 +18234,7 @@ function parseCollectionSliceEffect({
|
|
|
18317
18234
|
plan,
|
|
18318
18235
|
onProgress
|
|
18319
18236
|
}) {
|
|
18320
|
-
return
|
|
18237
|
+
return Effect27.gen(function* () {
|
|
18321
18238
|
const slice = kind === "like" ? "likes" : "bookmarks";
|
|
18322
18239
|
if (entries.length > 0) {
|
|
18323
18240
|
onProgress({ kind: "slice-start", slice, files: entries.length });
|
|
@@ -18367,7 +18284,7 @@ function parseCollectionSliceEffect({
|
|
|
18367
18284
|
}
|
|
18368
18285
|
|
|
18369
18286
|
// src/lib/archive/dm-slice.ts
|
|
18370
|
-
import { Effect as
|
|
18287
|
+
import { Effect as Effect28 } from "effect";
|
|
18371
18288
|
|
|
18372
18289
|
// src/lib/archive/profile-reconciler.ts
|
|
18373
18290
|
var defaultArchiveProfileMetadata = {
|
|
@@ -18602,7 +18519,7 @@ function parseDirectMessagesEffect({
|
|
|
18602
18519
|
onProgress
|
|
18603
18520
|
}) {
|
|
18604
18521
|
const { mentionDirectory, profiles, conversations, dmMessages } = plan;
|
|
18605
|
-
return
|
|
18522
|
+
return Effect28.gen(function* () {
|
|
18606
18523
|
const existingDmConversationAccounts = new Map(
|
|
18607
18524
|
db.prepare("select id, account_id from dm_conversations").all().map((row) => [row.id, row.account_id])
|
|
18608
18525
|
);
|
|
@@ -18836,7 +18753,7 @@ function parseDirectMessagesEffect({
|
|
|
18836
18753
|
}
|
|
18837
18754
|
|
|
18838
18755
|
// src/lib/archive/follow-slice.ts
|
|
18839
|
-
import { Effect as
|
|
18756
|
+
import { Effect as Effect29 } from "effect";
|
|
18840
18757
|
function parseFollowSliceEffect({
|
|
18841
18758
|
archivePath,
|
|
18842
18759
|
entries,
|
|
@@ -18847,7 +18764,7 @@ function parseFollowSliceEffect({
|
|
|
18847
18764
|
const key = direction === "followers" ? "follower" : "following";
|
|
18848
18765
|
const rows = direction === "followers" ? plan.followers : plan.following;
|
|
18849
18766
|
const ids = direction === "followers" ? plan.followerIds : plan.followingIds;
|
|
18850
|
-
return
|
|
18767
|
+
return Effect29.gen(function* () {
|
|
18851
18768
|
if (entries.length > 0) {
|
|
18852
18769
|
onProgress({
|
|
18853
18770
|
kind: "slice-start",
|
|
@@ -18897,14 +18814,14 @@ function reconcileFollowProfiles({
|
|
|
18897
18814
|
|
|
18898
18815
|
// src/lib/archive/tweet-slices.ts
|
|
18899
18816
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
18900
|
-
import { Effect as
|
|
18817
|
+
import { Effect as Effect30 } from "effect";
|
|
18901
18818
|
function parseTweetSliceEffect({
|
|
18902
18819
|
archivePath,
|
|
18903
18820
|
entries,
|
|
18904
18821
|
plan,
|
|
18905
18822
|
onProgress
|
|
18906
18823
|
}) {
|
|
18907
|
-
return
|
|
18824
|
+
return Effect30.gen(function* () {
|
|
18908
18825
|
if (entries.length > 0) {
|
|
18909
18826
|
onProgress({
|
|
18910
18827
|
kind: "slice-start",
|
|
@@ -18977,7 +18894,7 @@ function parseNoteTweetSliceEffect({
|
|
|
18977
18894
|
plan,
|
|
18978
18895
|
onProgress
|
|
18979
18896
|
}) {
|
|
18980
|
-
return
|
|
18897
|
+
return Effect30.gen(function* () {
|
|
18981
18898
|
if (entries.length > 0) {
|
|
18982
18899
|
onProgress({
|
|
18983
18900
|
kind: "slice-start",
|
|
@@ -19038,7 +18955,7 @@ import {
|
|
|
19038
18955
|
} from "node:fs";
|
|
19039
18956
|
import path6 from "node:path";
|
|
19040
18957
|
import { pipeline } from "node:stream/promises";
|
|
19041
|
-
import { Effect as
|
|
18958
|
+
import { Effect as Effect31 } from "effect";
|
|
19042
18959
|
var ARCHIVE_MEDIA_DIRECTORIES = [
|
|
19043
18960
|
{ directory: "tweets_media", kind: "tweets" },
|
|
19044
18961
|
{ directory: "direct_messages_media", kind: "dms" },
|
|
@@ -19124,7 +19041,7 @@ function copyArchiveEntryToFileEffect(archivePath, entryPath, destinationPath) {
|
|
|
19124
19041
|
});
|
|
19125
19042
|
}
|
|
19126
19043
|
function extractArchiveMediaFilesEffect(archivePath, selectedKinds) {
|
|
19127
|
-
return
|
|
19044
|
+
return Effect31.gen(function* () {
|
|
19128
19045
|
const counts = createArchiveMediaFileCounts();
|
|
19129
19046
|
if (selectedKinds?.size === 0) return counts;
|
|
19130
19047
|
const entries = yield* listArchiveEntryDetailsEffect(archivePath);
|
|
@@ -19207,7 +19124,7 @@ var ARCHIVE_IMPORT_SLICES = [
|
|
|
19207
19124
|
|
|
19208
19125
|
// src/lib/archive-import.ts
|
|
19209
19126
|
function importArchiveInternalEffect(archivePath, options = {}) {
|
|
19210
|
-
return
|
|
19127
|
+
return Effect32.gen(function* () {
|
|
19211
19128
|
const onProgress = options.onProgress ?? (() => {
|
|
19212
19129
|
});
|
|
19213
19130
|
const entries = yield* listArchiveEntriesEffect(archivePath);
|
|
@@ -19232,11 +19149,11 @@ function importArchiveInternalEffect(archivePath, options = {}) {
|
|
|
19232
19149
|
followingEntries
|
|
19233
19150
|
} = selectArchiveSliceEntries(entries, selection);
|
|
19234
19151
|
if (!accountEntry) {
|
|
19235
|
-
return yield*
|
|
19152
|
+
return yield* Effect32.fail(new Error("Archive missing data/account.js"));
|
|
19236
19153
|
}
|
|
19237
|
-
const [accountContent, profileContent] = yield*
|
|
19154
|
+
const [accountContent, profileContent] = yield* Effect32.all([
|
|
19238
19155
|
readArchiveEntryEffect(archivePath, accountEntry),
|
|
19239
|
-
profileEntry ? readArchiveEntryEffect(archivePath, profileEntry) :
|
|
19156
|
+
profileEntry ? readArchiveEntryEffect(archivePath, profileEntry) : Effect32.succeed("[]")
|
|
19240
19157
|
]);
|
|
19241
19158
|
const accountPayload = buildAccountPayload(
|
|
19242
19159
|
parseArchiveArray(accountContent)[0] ?? null,
|
|
@@ -19388,7 +19305,7 @@ function importArchive(archivePath, options = {}) {
|
|
|
19388
19305
|
}
|
|
19389
19306
|
|
|
19390
19307
|
// src/lib/profile-hydration.ts
|
|
19391
|
-
import { Effect as
|
|
19308
|
+
import { Effect as Effect33 } from "effect";
|
|
19392
19309
|
function asRecord3(value) {
|
|
19393
19310
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
19394
19311
|
}
|
|
@@ -19400,7 +19317,7 @@ function toError11(error) {
|
|
|
19400
19317
|
return error instanceof Error ? error : new Error(String(error));
|
|
19401
19318
|
}
|
|
19402
19319
|
function trySync7(try_) {
|
|
19403
|
-
return
|
|
19320
|
+
return Effect33.try({
|
|
19404
19321
|
try: try_,
|
|
19405
19322
|
catch: toError11
|
|
19406
19323
|
});
|
|
@@ -19408,9 +19325,9 @@ function trySync7(try_) {
|
|
|
19408
19325
|
var SEEDED_ACCOUNT_HANDLE = "steipete";
|
|
19409
19326
|
var SEEDED_ACCOUNT_EXTERNAL_USER_ID = "25401953";
|
|
19410
19327
|
function hydrateAccountFromBirdEffect() {
|
|
19411
|
-
return
|
|
19412
|
-
const account = yield*
|
|
19413
|
-
|
|
19328
|
+
return Effect33.gen(function* () {
|
|
19329
|
+
const account = yield* getAuthenticatedBirdAccountEffect().pipe(
|
|
19330
|
+
Effect33.catchAll(() => Effect33.succeed(null))
|
|
19414
19331
|
);
|
|
19415
19332
|
if (!account?.username) return false;
|
|
19416
19333
|
const handle = account.username.replace(/^@/, "");
|
|
@@ -19468,8 +19385,8 @@ function hydrateAccountFromBirdEffect() {
|
|
|
19468
19385
|
});
|
|
19469
19386
|
}
|
|
19470
19387
|
function hydrateProfilesFromXEffect() {
|
|
19471
|
-
return
|
|
19472
|
-
const transport = yield*
|
|
19388
|
+
return Effect33.gen(function* () {
|
|
19389
|
+
const transport = yield* getTransportStatusEffect();
|
|
19473
19390
|
if (transport.availableTransport !== "xurl") {
|
|
19474
19391
|
const hydratedAccount2 = yield* hydrateAccountFromBirdEffect();
|
|
19475
19392
|
return {
|
|
@@ -19531,7 +19448,7 @@ function hydrateProfilesFromXEffect() {
|
|
|
19531
19448
|
let hydratedProfiles = 0;
|
|
19532
19449
|
for (let index = 0; index < candidateIds.length; index += 100) {
|
|
19533
19450
|
const batch = candidateIds.slice(index, index + 100);
|
|
19534
|
-
const users = yield*
|
|
19451
|
+
const users = yield* lookupUsersByIdsEffect(batch);
|
|
19535
19452
|
yield* trySync7(() => {
|
|
19536
19453
|
db.transaction(() => {
|
|
19537
19454
|
for (const user of users) {
|
|
@@ -19548,8 +19465,8 @@ function hydrateProfilesFromXEffect() {
|
|
|
19548
19465
|
});
|
|
19549
19466
|
}
|
|
19550
19467
|
let hydratedAccount = false;
|
|
19551
|
-
const me = yield*
|
|
19552
|
-
|
|
19468
|
+
const me = yield* lookupAuthenticatedUserEffect().pipe(
|
|
19469
|
+
Effect33.catchAll(() => Effect33.succeed(null))
|
|
19553
19470
|
);
|
|
19554
19471
|
if (me) {
|
|
19555
19472
|
const metrics = asRecord3(me.public_metrics);
|
|
@@ -19769,7 +19686,7 @@ function registerCoreCommands({
|
|
|
19769
19686
|
}
|
|
19770
19687
|
|
|
19771
19688
|
// src/lib/dms-live.ts
|
|
19772
|
-
import { Effect as
|
|
19689
|
+
import { Effect as Effect34 } from "effect";
|
|
19773
19690
|
var DEFAULT_DMS_CACHE_TTL_MS = 2 * 6e4;
|
|
19774
19691
|
var PREVIEW_MESSAGE_ID_PREFIX = "preview:";
|
|
19775
19692
|
var XURL_DMS_MAX_RESULTS = 100;
|
|
@@ -20246,7 +20163,7 @@ function fetchDirectMessagesViaXurlEffect({
|
|
|
20246
20163
|
allPages,
|
|
20247
20164
|
pageDelayMs
|
|
20248
20165
|
}) {
|
|
20249
|
-
return
|
|
20166
|
+
return Effect34.gen(function* () {
|
|
20250
20167
|
const pageLimit = allPages ? Number.POSITIVE_INFINITY : Math.max(1, (maxPages ?? 0) + 1);
|
|
20251
20168
|
const result = yield* runSyncPlanEffect({
|
|
20252
20169
|
fetchPage: ({ cursor }) => liveTransportGateway.xurl.listDirectMessages({
|
|
@@ -20272,7 +20189,7 @@ function syncDirectMessagesViaCachedBirdEffect({
|
|
|
20272
20189
|
refresh = false,
|
|
20273
20190
|
cacheTtlMs
|
|
20274
20191
|
} = {}) {
|
|
20275
|
-
return
|
|
20192
|
+
return Effect34.gen(function* () {
|
|
20276
20193
|
const parsedMode = parseSyncMode2(mode);
|
|
20277
20194
|
if (parsedMode === "xurl") {
|
|
20278
20195
|
assertXurlLimit2(limit);
|
|
@@ -20301,14 +20218,14 @@ function syncDirectMessagesViaCachedBirdEffect({
|
|
|
20301
20218
|
} else {
|
|
20302
20219
|
const tryXurl = (parsedMode === "xurl" || parsedMode === "auto") && inbox !== "requests";
|
|
20303
20220
|
if (tryXurl) {
|
|
20304
|
-
const xurlPayload = yield*
|
|
20221
|
+
const xurlPayload = yield* Effect34.gen(function* () {
|
|
20305
20222
|
const authenticated = getAuthenticatedXurlAccount(
|
|
20306
20223
|
yield* liveTransportGateway.xurl.lookupAuthenticatedOAuth2User(
|
|
20307
20224
|
resolvedAccount.username
|
|
20308
20225
|
)
|
|
20309
20226
|
);
|
|
20310
20227
|
if (!authenticated.username && !authenticated.id) {
|
|
20311
|
-
return yield*
|
|
20228
|
+
return yield* Effect34.fail(
|
|
20312
20229
|
new Error("xurl authenticated user unavailable")
|
|
20313
20230
|
);
|
|
20314
20231
|
}
|
|
@@ -20327,7 +20244,7 @@ function syncDirectMessagesViaCachedBirdEffect({
|
|
|
20327
20244
|
);
|
|
20328
20245
|
}
|
|
20329
20246
|
if (!accountExternalUserId) {
|
|
20330
|
-
return yield*
|
|
20247
|
+
return yield* Effect34.fail(
|
|
20331
20248
|
new Error(
|
|
20332
20249
|
"xurl authenticated user id unavailable; refusing to sync DMs"
|
|
20333
20250
|
)
|
|
@@ -20341,9 +20258,9 @@ function syncDirectMessagesViaCachedBirdEffect({
|
|
|
20341
20258
|
...typeof pageDelayMs === "number" ? { pageDelayMs } : {}
|
|
20342
20259
|
});
|
|
20343
20260
|
}).pipe(
|
|
20344
|
-
|
|
20345
|
-
if (parsedMode === "xurl") return
|
|
20346
|
-
return
|
|
20261
|
+
Effect34.catchAll((error) => {
|
|
20262
|
+
if (parsedMode === "xurl") return Effect34.fail(error);
|
|
20263
|
+
return Effect34.succeed(void 0);
|
|
20347
20264
|
})
|
|
20348
20265
|
);
|
|
20349
20266
|
if (xurlPayload) {
|
|
@@ -20414,15 +20331,15 @@ function syncDirectMessagesViaCachedBird(options = {}) {
|
|
|
20414
20331
|
}
|
|
20415
20332
|
|
|
20416
20333
|
// src/lib/profile-resolver.ts
|
|
20417
|
-
import { Effect as
|
|
20334
|
+
import { Effect as Effect36 } from "effect";
|
|
20418
20335
|
|
|
20419
20336
|
// src/lib/profile-affiliation-hydration.ts
|
|
20420
|
-
import { Effect as
|
|
20337
|
+
import { Effect as Effect35 } from "effect";
|
|
20421
20338
|
function toError12(error) {
|
|
20422
20339
|
return error instanceof Error ? error : new Error(String(error));
|
|
20423
20340
|
}
|
|
20424
20341
|
function trySync8(try_) {
|
|
20425
|
-
return
|
|
20342
|
+
return Effect35.try({
|
|
20426
20343
|
try: try_,
|
|
20427
20344
|
catch: toError12
|
|
20428
20345
|
});
|
|
@@ -20499,7 +20416,7 @@ function findLocalOrganizationProfileId(db, handle) {
|
|
|
20499
20416
|
return row?.id ?? null;
|
|
20500
20417
|
}
|
|
20501
20418
|
function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
|
|
20502
|
-
return
|
|
20419
|
+
return Effect35.gen(function* () {
|
|
20503
20420
|
const rows = yield* trySync8(
|
|
20504
20421
|
() => db.prepare(
|
|
20505
20422
|
`
|
|
@@ -20526,7 +20443,7 @@ function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
|
|
|
20526
20443
|
result.skipped += 1;
|
|
20527
20444
|
continue;
|
|
20528
20445
|
}
|
|
20529
|
-
const hydrated = yield*
|
|
20446
|
+
const hydrated = yield* Effect35.gen(function* () {
|
|
20530
20447
|
const localOrganizationProfileId = yield* trySync8(
|
|
20531
20448
|
() => findLocalOrganizationProfileId(db, handle)
|
|
20532
20449
|
);
|
|
@@ -20557,12 +20474,12 @@ function hydrateProfileAffiliationOrganizationsEffect(db, subjectProfileId) {
|
|
|
20557
20474
|
result.hydrated += 1;
|
|
20558
20475
|
return true;
|
|
20559
20476
|
}).pipe(
|
|
20560
|
-
|
|
20477
|
+
Effect35.catchAll((error) => {
|
|
20561
20478
|
result.errors.push({
|
|
20562
20479
|
handle,
|
|
20563
20480
|
error: error instanceof Error ? error.message : String(error)
|
|
20564
20481
|
});
|
|
20565
|
-
return
|
|
20482
|
+
return Effect35.succeed(false);
|
|
20566
20483
|
})
|
|
20567
20484
|
);
|
|
20568
20485
|
if (!hydrated) {
|
|
@@ -20587,7 +20504,7 @@ function toError13(error) {
|
|
|
20587
20504
|
return error instanceof Error ? error : new Error(String(error));
|
|
20588
20505
|
}
|
|
20589
20506
|
function trySync9(try_) {
|
|
20590
|
-
return
|
|
20507
|
+
return Effect36.try({
|
|
20591
20508
|
try: try_,
|
|
20592
20509
|
catch: toError13
|
|
20593
20510
|
});
|
|
@@ -20629,16 +20546,15 @@ function updateConversationTitles(profile, db = getNativeDb()) {
|
|
|
20629
20546
|
).run(profile.displayName || profile.handle, profile.id);
|
|
20630
20547
|
}
|
|
20631
20548
|
function lookupViaXurlEffect(externalUserId) {
|
|
20632
|
-
return
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
});
|
|
20549
|
+
return lookupUsersByIdsEffect([externalUserId]).pipe(
|
|
20550
|
+
Effect36.map(([user]) => user ?? null)
|
|
20551
|
+
);
|
|
20636
20552
|
}
|
|
20637
20553
|
function fetchProfileUserEffect(externalUserId, xurlFallback) {
|
|
20638
|
-
return
|
|
20554
|
+
return Effect36.gen(function* () {
|
|
20639
20555
|
const birdResult = yield* lookupProfileViaBirdEffect(externalUserId).pipe(
|
|
20640
|
-
|
|
20641
|
-
|
|
20556
|
+
Effect36.map((user) => ({ ok: true, user })),
|
|
20557
|
+
Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
|
|
20642
20558
|
);
|
|
20643
20559
|
if (birdResult.ok) {
|
|
20644
20560
|
const birdUser = birdResult.user;
|
|
@@ -20656,8 +20572,8 @@ function fetchProfileUserEffect(externalUserId, xurlFallback) {
|
|
|
20656
20572
|
return { status: "miss", source: "bird" };
|
|
20657
20573
|
}
|
|
20658
20574
|
const xurlResult = yield* lookupViaXurlEffect(externalUserId).pipe(
|
|
20659
|
-
|
|
20660
|
-
|
|
20575
|
+
Effect36.map((user) => ({ ok: true, user })),
|
|
20576
|
+
Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
|
|
20661
20577
|
);
|
|
20662
20578
|
if (xurlResult.ok) {
|
|
20663
20579
|
const xurlUser = xurlResult.user;
|
|
@@ -20671,13 +20587,13 @@ function fetchProfileUserEffect(externalUserId, xurlFallback) {
|
|
|
20671
20587
|
});
|
|
20672
20588
|
}
|
|
20673
20589
|
function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
|
|
20674
|
-
return
|
|
20590
|
+
return Effect36.gen(function* () {
|
|
20675
20591
|
const uniqueIds = Array.from(new Set(externalUserIds));
|
|
20676
20592
|
const results = /* @__PURE__ */ new Map();
|
|
20677
20593
|
let unresolved = uniqueIds;
|
|
20678
20594
|
const birdResult = yield* lookupProfilesViaBirdEffect(uniqueIds).pipe(
|
|
20679
|
-
|
|
20680
|
-
|
|
20595
|
+
Effect36.map((items) => ({ ok: true, items })),
|
|
20596
|
+
Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
|
|
20681
20597
|
);
|
|
20682
20598
|
if (birdResult.ok) {
|
|
20683
20599
|
const birdResults = birdResult.items;
|
|
@@ -20717,11 +20633,9 @@ function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
|
|
|
20717
20633
|
}
|
|
20718
20634
|
return results;
|
|
20719
20635
|
}
|
|
20720
|
-
const xurlResult = yield*
|
|
20721
|
-
() =>
|
|
20722
|
-
|
|
20723
|
-
Effect37.map((users) => ({ ok: true, users })),
|
|
20724
|
-
Effect37.catchAll((error) => Effect37.succeed({ ok: false, error }))
|
|
20636
|
+
const xurlResult = yield* lookupUsersByIdsEffect(unresolved).pipe(
|
|
20637
|
+
Effect36.map((users) => ({ ok: true, users })),
|
|
20638
|
+
Effect36.catchAll((error) => Effect36.succeed({ ok: false, error }))
|
|
20725
20639
|
);
|
|
20726
20640
|
if (xurlResult.ok) {
|
|
20727
20641
|
const xurlUsers = xurlResult.users;
|
|
@@ -20748,7 +20662,7 @@ function fetchProfileUsersEffect(externalUserIds, xurlFallback) {
|
|
|
20748
20662
|
});
|
|
20749
20663
|
}
|
|
20750
20664
|
function resolveProfilesForIdsEffect(profileIds, options = {}) {
|
|
20751
|
-
return
|
|
20665
|
+
return Effect36.gen(function* () {
|
|
20752
20666
|
const db = yield* trySync9(() => getNativeDb());
|
|
20753
20667
|
const maxAgeMs = options.maxAgeMs ?? PROFILE_CACHE_TTL_MS;
|
|
20754
20668
|
const negativeMaxAgeMs = options.negativeMaxAgeMs ?? PROFILE_NEGATIVE_CACHE_TTL_MS;
|
|
@@ -20870,7 +20784,7 @@ function resolveProfilesForIds(profileIds, options = {}) {
|
|
|
20870
20784
|
}
|
|
20871
20785
|
|
|
20872
20786
|
// src/lib/url-expansion.ts
|
|
20873
|
-
import { Effect as
|
|
20787
|
+
import { Effect as Effect38 } from "effect";
|
|
20874
20788
|
|
|
20875
20789
|
// src/lib/link-preview-metadata.ts
|
|
20876
20790
|
import { lookup } from "node:dns/promises";
|
|
@@ -20878,7 +20792,8 @@ import http from "node:http";
|
|
|
20878
20792
|
import https from "node:https";
|
|
20879
20793
|
import net from "node:net";
|
|
20880
20794
|
import { Readable } from "node:stream";
|
|
20881
|
-
import {
|
|
20795
|
+
import { createBrotliDecompress, createGunzip, createInflate } from "node:zlib";
|
|
20796
|
+
import { Effect as Effect37 } from "effect";
|
|
20882
20797
|
|
|
20883
20798
|
// src/lib/url-expansion-store.ts
|
|
20884
20799
|
function getTweetTarget(url) {
|
|
@@ -20962,42 +20877,89 @@ function upsertUrlExpansion(db, item) {
|
|
|
20962
20877
|
|
|
20963
20878
|
// src/lib/link-preview-metadata.ts
|
|
20964
20879
|
var FETCH_TIMEOUT_MS = 12e3;
|
|
20880
|
+
var MAX_HTML_CHARS = 2e6;
|
|
20965
20881
|
var MAX_REDIRECTS = 4;
|
|
20966
20882
|
var NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([204, 205, 304]);
|
|
20883
|
+
function cleanText(value) {
|
|
20884
|
+
if (!value) return null;
|
|
20885
|
+
const cleaned = decodeHtmlEntities(value).replace(/\s+/g, " ").trim();
|
|
20886
|
+
return cleaned.length > 0 ? cleaned : null;
|
|
20887
|
+
}
|
|
20967
20888
|
function decodeCodePoint(value, fallback) {
|
|
20968
20889
|
try {
|
|
20969
20890
|
return String.fromCodePoint(value);
|
|
20970
20891
|
} catch {
|
|
20971
|
-
return fallback;
|
|
20892
|
+
return fallback;
|
|
20893
|
+
}
|
|
20894
|
+
}
|
|
20895
|
+
function decodeHtmlEntities(value) {
|
|
20896
|
+
return value.replace(
|
|
20897
|
+
/&#(\d+);/g,
|
|
20898
|
+
(entity, code) => decodeCodePoint(Number(code), entity)
|
|
20899
|
+
).replace(
|
|
20900
|
+
/&#x([a-f0-9]+);/gi,
|
|
20901
|
+
(entity, code) => decodeCodePoint(Number.parseInt(code, 16), entity)
|
|
20902
|
+
).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'");
|
|
20903
|
+
}
|
|
20904
|
+
function parseAttributes(tag) {
|
|
20905
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
20906
|
+
for (const match of tag.matchAll(
|
|
20907
|
+
/([a-zA-Z_:][-a-zA-Z0-9_:.]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/g
|
|
20908
|
+
)) {
|
|
20909
|
+
const key = match[1]?.toLowerCase();
|
|
20910
|
+
const value = match[3] ?? match[4] ?? match[5] ?? "";
|
|
20911
|
+
if (key) {
|
|
20912
|
+
attributes.set(key, value);
|
|
20913
|
+
}
|
|
20914
|
+
}
|
|
20915
|
+
return attributes;
|
|
20916
|
+
}
|
|
20917
|
+
function metaContents(html) {
|
|
20918
|
+
const values = /* @__PURE__ */ new Map();
|
|
20919
|
+
for (const match of html.matchAll(/<meta\b[^>]*>/gi)) {
|
|
20920
|
+
const attributes = parseAttributes(match[0]);
|
|
20921
|
+
const key = (attributes.get("property") ?? attributes.get("name") ?? "").toLowerCase();
|
|
20922
|
+
const content = cleanText(
|
|
20923
|
+
attributes.get("content") ?? attributes.get("value") ?? ""
|
|
20924
|
+
);
|
|
20925
|
+
if (key && content && !values.has(key)) {
|
|
20926
|
+
values.set(key, content);
|
|
20927
|
+
}
|
|
20928
|
+
}
|
|
20929
|
+
return values;
|
|
20930
|
+
}
|
|
20931
|
+
function pick(values, keys) {
|
|
20932
|
+
for (const key of keys) {
|
|
20933
|
+
const value = cleanText(values.get(key));
|
|
20934
|
+
if (value) return value;
|
|
20935
|
+
}
|
|
20936
|
+
return null;
|
|
20937
|
+
}
|
|
20938
|
+
function titleFromHtml(html) {
|
|
20939
|
+
const match = html.match(/<title\b[^>]*>([\s\S]*?)<\/title>/i);
|
|
20940
|
+
return cleanText(match?.[1]);
|
|
20941
|
+
}
|
|
20942
|
+
function absoluteUrl(value, baseUrl) {
|
|
20943
|
+
if (!value) return null;
|
|
20944
|
+
try {
|
|
20945
|
+
return new URL(value, baseUrl).toString();
|
|
20946
|
+
} catch {
|
|
20947
|
+
return null;
|
|
20948
|
+
}
|
|
20949
|
+
}
|
|
20950
|
+
function hostLabel(url) {
|
|
20951
|
+
try {
|
|
20952
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
20953
|
+
} catch {
|
|
20954
|
+
return url;
|
|
20972
20955
|
}
|
|
20973
20956
|
}
|
|
20974
|
-
function decodeHtmlEntities(value) {
|
|
20975
|
-
return value.replace(
|
|
20976
|
-
/&#(\d+);/g,
|
|
20977
|
-
(entity, code) => decodeCodePoint(Number(code), entity)
|
|
20978
|
-
).replace(
|
|
20979
|
-
/&#x([a-f0-9]+);/gi,
|
|
20980
|
-
(entity, code) => decodeCodePoint(Number.parseInt(code, 16), entity)
|
|
20981
|
-
).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'");
|
|
20982
|
-
}
|
|
20983
20957
|
function isInjectedFetchAllowed() {
|
|
20984
20958
|
return process.env.NODE_ENV === "test" || process.env.VITEST === "true";
|
|
20985
20959
|
}
|
|
20986
20960
|
function stripAddressBrackets(value) {
|
|
20987
20961
|
return value.replace(/^\[|\]$/g, "");
|
|
20988
20962
|
}
|
|
20989
|
-
function withTimeout(promise, ms) {
|
|
20990
|
-
let timer = null;
|
|
20991
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
20992
|
-
timer = setTimeout(
|
|
20993
|
-
() => reject(new Error("Link preview request timed out")),
|
|
20994
|
-
ms
|
|
20995
|
-
);
|
|
20996
|
-
});
|
|
20997
|
-
return Promise.race([promise, timeout]).finally(() => {
|
|
20998
|
-
if (timer) clearTimeout(timer);
|
|
20999
|
-
});
|
|
21000
|
-
}
|
|
21001
20963
|
async function resolvePublicAddresses(hostname) {
|
|
21002
20964
|
const normalized = stripAddressBrackets(hostname);
|
|
21003
20965
|
if (net.isIP(normalized)) return [normalized];
|
|
@@ -21012,22 +20974,34 @@ function validateResolvedAddresses(addresses) {
|
|
|
21012
20974
|
throw new Error("Link preview URL resolves to a private address");
|
|
21013
20975
|
}
|
|
21014
20976
|
}
|
|
21015
|
-
|
|
21016
|
-
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21023
|
-
|
|
21024
|
-
|
|
21025
|
-
|
|
21026
|
-
|
|
21027
|
-
|
|
21028
|
-
|
|
21029
|
-
|
|
21030
|
-
|
|
20977
|
+
var resolveSafeAddressesEffect = Effect37.fn(
|
|
20978
|
+
"linkPreview.resolveSafeAddresses"
|
|
20979
|
+
)(
|
|
20980
|
+
(hostname, resolveHost, timeoutMs) => tryPromise(() => resolveHost(stripAddressBrackets(hostname))).pipe(
|
|
20981
|
+
Effect37.timeoutFail({
|
|
20982
|
+
duration: timeoutMs,
|
|
20983
|
+
onTimeout: () => new Error("Link preview request timed out")
|
|
20984
|
+
}),
|
|
20985
|
+
Effect37.flatMap(
|
|
20986
|
+
(addresses) => Effect37.try({
|
|
20987
|
+
try: () => {
|
|
20988
|
+
validateResolvedAddresses(addresses);
|
|
20989
|
+
return addresses.map((address) => {
|
|
20990
|
+
const normalized = stripAddressBrackets(address);
|
|
20991
|
+
const family = net.isIP(normalized);
|
|
20992
|
+
if (family !== 4 && family !== 6) {
|
|
20993
|
+
throw new Error(
|
|
20994
|
+
"Link preview host resolved to an invalid address"
|
|
20995
|
+
);
|
|
20996
|
+
}
|
|
20997
|
+
return { address: normalized, family };
|
|
20998
|
+
});
|
|
20999
|
+
},
|
|
21000
|
+
catch: (error) => error
|
|
21001
|
+
})
|
|
21002
|
+
)
|
|
21003
|
+
)
|
|
21004
|
+
);
|
|
21031
21005
|
function headersFromIncoming(headers) {
|
|
21032
21006
|
const result = new Headers();
|
|
21033
21007
|
for (const [key, value] of Object.entries(headers)) {
|
|
@@ -21039,9 +21013,30 @@ function headersFromIncoming(headers) {
|
|
|
21039
21013
|
}
|
|
21040
21014
|
return result;
|
|
21041
21015
|
}
|
|
21016
|
+
function decodedResponseBody(response) {
|
|
21017
|
+
const body = response.body;
|
|
21018
|
+
if (!body) return null;
|
|
21019
|
+
const encoding = response.headers.get("content-encoding")?.split(",").at(-1)?.trim().toLowerCase();
|
|
21020
|
+
if (!encoding || encoding === "identity") return body;
|
|
21021
|
+
const nodeBody = Readable.fromWeb(
|
|
21022
|
+
body
|
|
21023
|
+
);
|
|
21024
|
+
if (encoding === "gzip" || encoding === "x-gzip") {
|
|
21025
|
+
return Readable.toWeb(nodeBody.pipe(createGunzip()));
|
|
21026
|
+
}
|
|
21027
|
+
if (encoding === "br") {
|
|
21028
|
+
return Readable.toWeb(
|
|
21029
|
+
nodeBody.pipe(createBrotliDecompress())
|
|
21030
|
+
);
|
|
21031
|
+
}
|
|
21032
|
+
if (encoding === "deflate") {
|
|
21033
|
+
return Readable.toWeb(nodeBody.pipe(createInflate()));
|
|
21034
|
+
}
|
|
21035
|
+
return body;
|
|
21036
|
+
}
|
|
21042
21037
|
function cancelResponseBodyEffect(response) {
|
|
21043
21038
|
return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
|
|
21044
|
-
|
|
21039
|
+
Effect37.catchAll(() => Effect37.void)
|
|
21045
21040
|
);
|
|
21046
21041
|
}
|
|
21047
21042
|
function respondWithResolvedAddress(selected, lookupOptions, callback) {
|
|
@@ -21135,61 +21130,67 @@ function nodeSafeFetch(url, options) {
|
|
|
21135
21130
|
Promise.reject(new Error("Link preview host did not resolve"))
|
|
21136
21131
|
);
|
|
21137
21132
|
}
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
|
|
21133
|
+
var safePreviewFetchEffect = Effect37.fn("linkPreview.safePreviewFetch")(
|
|
21134
|
+
function* (url, options) {
|
|
21135
|
+
const resolveHost = options.resolveHost ?? (options.fetchImpl ? null : (hostname) => resolvePublicAddresses(hostname));
|
|
21136
|
+
const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
21137
|
+
const method = options.method ?? "GET";
|
|
21138
|
+
const deadline = Date.now() + timeoutMs;
|
|
21139
|
+
const remainingTimeoutMs = () => Math.max(1, deadline - Date.now());
|
|
21140
|
+
const headers = {
|
|
21141
|
+
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36 birdclaw/0.4",
|
|
21142
|
+
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
21143
|
+
"accept-language": "en-US,en;q=0.9"
|
|
21144
|
+
};
|
|
21150
21145
|
if (options.fetchImpl && !isInjectedFetchAllowed()) {
|
|
21151
|
-
return yield*
|
|
21146
|
+
return yield* Effect37.fail(
|
|
21152
21147
|
new Error("Custom link preview fetch is only available in tests")
|
|
21153
21148
|
);
|
|
21154
21149
|
}
|
|
21155
21150
|
let currentUrl = url;
|
|
21156
21151
|
for (let redirect = 0; redirect <= MAX_REDIRECTS; redirect += 1) {
|
|
21157
21152
|
if (Date.now() >= deadline) {
|
|
21158
|
-
return yield*
|
|
21153
|
+
return yield* Effect37.fail(new Error("Link preview request timed out"));
|
|
21159
21154
|
}
|
|
21160
|
-
|
|
21161
|
-
const parsed = yield* Effect38.try({
|
|
21155
|
+
const parsed = yield* Effect37.try({
|
|
21162
21156
|
try: () => assertSafePreviewUrl(currentUrl),
|
|
21163
21157
|
catch: (error) => error
|
|
21164
21158
|
});
|
|
21165
21159
|
if (options.fetchImpl && resolveHost) {
|
|
21166
|
-
yield*
|
|
21167
|
-
|
|
21160
|
+
yield* resolveSafeAddressesEffect(
|
|
21161
|
+
parsed.hostname,
|
|
21162
|
+
resolveHost,
|
|
21163
|
+
remainingTimeoutMs()
|
|
21168
21164
|
);
|
|
21169
21165
|
}
|
|
21170
21166
|
if (Date.now() >= deadline) {
|
|
21171
|
-
return yield*
|
|
21167
|
+
return yield* Effect37.fail(new Error("Link preview request timed out"));
|
|
21172
21168
|
}
|
|
21173
|
-
remainingMs = remainingTimeoutMs();
|
|
21174
21169
|
const response = options.fetchImpl ? yield* tryPromise(
|
|
21175
21170
|
() => options.fetchImpl?.(parsed.toString(), {
|
|
21176
21171
|
headers,
|
|
21177
21172
|
method,
|
|
21178
21173
|
redirect: "manual",
|
|
21179
|
-
signal: AbortSignal.timeout(
|
|
21174
|
+
signal: AbortSignal.timeout(remainingTimeoutMs())
|
|
21180
21175
|
})
|
|
21181
|
-
) : yield*
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
|
|
21187
|
-
(
|
|
21188
|
-
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
|
|
21176
|
+
) : yield* resolveSafeAddressesEffect(
|
|
21177
|
+
parsed.hostname,
|
|
21178
|
+
options.resolveHost ?? resolvePublicAddresses,
|
|
21179
|
+
remainingTimeoutMs()
|
|
21180
|
+
).pipe(
|
|
21181
|
+
Effect37.filterOrFail(
|
|
21182
|
+
() => Date.now() < deadline,
|
|
21183
|
+
() => new Error("Link preview request timed out")
|
|
21184
|
+
),
|
|
21185
|
+
Effect37.flatMap(
|
|
21186
|
+
(addresses) => tryPromise(
|
|
21187
|
+
() => nodeSafeFetch(parsed, {
|
|
21188
|
+
addresses,
|
|
21189
|
+
headers,
|
|
21190
|
+
method,
|
|
21191
|
+
timeoutMs: remainingTimeoutMs()
|
|
21192
|
+
})
|
|
21193
|
+
)
|
|
21193
21194
|
)
|
|
21194
21195
|
);
|
|
21195
21196
|
if (response.status < 300 || response.status >= 400) return response;
|
|
@@ -21197,20 +21198,58 @@ function safePreviewFetchEffect(url, options) {
|
|
|
21197
21198
|
if (!location) return response;
|
|
21198
21199
|
yield* cancelResponseBodyEffect(response);
|
|
21199
21200
|
if (redirect === MAX_REDIRECTS) {
|
|
21200
|
-
return yield*
|
|
21201
|
+
return yield* Effect37.fail(
|
|
21201
21202
|
new Error("Link preview redirected too many times")
|
|
21202
21203
|
);
|
|
21203
21204
|
}
|
|
21204
|
-
const nextUrl = yield*
|
|
21205
|
+
const nextUrl = yield* Effect37.try({
|
|
21205
21206
|
try: () => new URL(location, parsed).toString(),
|
|
21206
21207
|
catch: (error) => error
|
|
21207
21208
|
});
|
|
21208
21209
|
currentUrl = nextUrl;
|
|
21209
21210
|
}
|
|
21210
|
-
return yield*
|
|
21211
|
+
return yield* Effect37.fail(
|
|
21211
21212
|
new Error("Link preview redirected too many times")
|
|
21212
21213
|
);
|
|
21213
|
-
}
|
|
21214
|
+
}
|
|
21215
|
+
);
|
|
21216
|
+
function readResponseTextEffect(response) {
|
|
21217
|
+
const contentLength2 = Number(response.headers.get("content-length") ?? 0);
|
|
21218
|
+
if (Number.isFinite(contentLength2) && contentLength2 > MAX_HTML_CHARS) {
|
|
21219
|
+
return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
|
|
21220
|
+
Effect37.flatMap(
|
|
21221
|
+
() => Effect37.fail(new Error("Link preview response is too large"))
|
|
21222
|
+
)
|
|
21223
|
+
);
|
|
21224
|
+
}
|
|
21225
|
+
const reader = decodedResponseBody(response)?.getReader();
|
|
21226
|
+
if (!reader) return tryPromise(() => response.text());
|
|
21227
|
+
const decoder = new TextDecoder();
|
|
21228
|
+
let total = 0;
|
|
21229
|
+
let text = "";
|
|
21230
|
+
return Effect37.gen(function* () {
|
|
21231
|
+
for (; ; ) {
|
|
21232
|
+
const { done, value } = yield* tryPromise(() => reader.read());
|
|
21233
|
+
if (done) break;
|
|
21234
|
+
total += value.byteLength;
|
|
21235
|
+
if (total > MAX_HTML_CHARS) {
|
|
21236
|
+
yield* tryPromise(
|
|
21237
|
+
() => reader.cancel(new Error("Link preview response is too large"))
|
|
21238
|
+
);
|
|
21239
|
+
return yield* Effect37.fail(
|
|
21240
|
+
new Error("Link preview response is too large")
|
|
21241
|
+
);
|
|
21242
|
+
}
|
|
21243
|
+
text += decoder.decode(value, { stream: true });
|
|
21244
|
+
}
|
|
21245
|
+
return text + decoder.decode();
|
|
21246
|
+
}).pipe(
|
|
21247
|
+
Effect37.ensuring(
|
|
21248
|
+
Effect37.sync(() => {
|
|
21249
|
+
reader.releaseLock();
|
|
21250
|
+
})
|
|
21251
|
+
)
|
|
21252
|
+
);
|
|
21214
21253
|
}
|
|
21215
21254
|
function youtubeThumbnail(url) {
|
|
21216
21255
|
try {
|
|
@@ -21234,6 +21273,157 @@ function youtubeThumbnail(url) {
|
|
|
21234
21273
|
return null;
|
|
21235
21274
|
}
|
|
21236
21275
|
}
|
|
21276
|
+
function extractLinkPreviewMetadata(html, url) {
|
|
21277
|
+
const meta = metaContents(html);
|
|
21278
|
+
const title = pick(meta, ["og:title", "twitter:title"]) ?? titleFromHtml(html) ?? hostLabel(url);
|
|
21279
|
+
const description = pick(meta, [
|
|
21280
|
+
"og:description",
|
|
21281
|
+
"twitter:description",
|
|
21282
|
+
"description"
|
|
21283
|
+
]);
|
|
21284
|
+
const siteName = pick(meta, ["og:site_name", "application-name"]) ?? hostLabel(url);
|
|
21285
|
+
const image = pick(meta, [
|
|
21286
|
+
"og:image:secure_url",
|
|
21287
|
+
"og:image:url",
|
|
21288
|
+
"og:image",
|
|
21289
|
+
"twitter:image:src",
|
|
21290
|
+
"twitter:image"
|
|
21291
|
+
]) ?? youtubeThumbnail(url);
|
|
21292
|
+
return {
|
|
21293
|
+
url,
|
|
21294
|
+
title,
|
|
21295
|
+
description,
|
|
21296
|
+
imageUrl: absoluteUrl(image, url),
|
|
21297
|
+
siteName
|
|
21298
|
+
};
|
|
21299
|
+
}
|
|
21300
|
+
var fetchLinkPreviewMetadataEffect = Effect37.fn(
|
|
21301
|
+
"linkPreview.fetchMetadata"
|
|
21302
|
+
)(
|
|
21303
|
+
function* (url, _options = {}) {
|
|
21304
|
+
const response = yield* safePreviewFetchEffect(url, _options);
|
|
21305
|
+
const finalUrl = response.url || url;
|
|
21306
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
21307
|
+
if (!response.ok) {
|
|
21308
|
+
yield* cancelResponseBodyEffect(response);
|
|
21309
|
+
return yield* Effect37.fail(new Error(`HTTP ${response.status}`));
|
|
21310
|
+
}
|
|
21311
|
+
if (contentType.toLowerCase().startsWith("image/")) {
|
|
21312
|
+
yield* cancelResponseBodyEffect(response);
|
|
21313
|
+
return {
|
|
21314
|
+
url: finalUrl,
|
|
21315
|
+
title: hostLabel(finalUrl),
|
|
21316
|
+
description: null,
|
|
21317
|
+
imageUrl: finalUrl,
|
|
21318
|
+
siteName: hostLabel(finalUrl)
|
|
21319
|
+
};
|
|
21320
|
+
}
|
|
21321
|
+
const content = yield* readResponseTextEffect(response);
|
|
21322
|
+
return yield* Effect37.try({
|
|
21323
|
+
try: () => extractLinkPreviewMetadata(content.slice(0, MAX_HTML_CHARS), finalUrl),
|
|
21324
|
+
catch: (error) => error
|
|
21325
|
+
});
|
|
21326
|
+
},
|
|
21327
|
+
// Any failure degrades to a host-label preview carrying the error message.
|
|
21328
|
+
(effect, url) => Effect37.catchAll(
|
|
21329
|
+
effect,
|
|
21330
|
+
(error) => Effect37.succeed({
|
|
21331
|
+
url,
|
|
21332
|
+
title: hostLabel(url),
|
|
21333
|
+
description: null,
|
|
21334
|
+
imageUrl: youtubeThumbnail(url),
|
|
21335
|
+
siteName: hostLabel(url),
|
|
21336
|
+
error: error instanceof Error ? error.message : String(error)
|
|
21337
|
+
})
|
|
21338
|
+
)
|
|
21339
|
+
);
|
|
21340
|
+
function readCachedPreview(db, url, shortUrl) {
|
|
21341
|
+
return db.prepare(
|
|
21342
|
+
`
|
|
21343
|
+
select short_url, expanded_url, final_url, status, title, description,
|
|
21344
|
+
image_url, site_name, error, source, updated_at
|
|
21345
|
+
from url_expansions
|
|
21346
|
+
where short_url in (?, ?)
|
|
21347
|
+
or expanded_url in (?, ?)
|
|
21348
|
+
or final_url in (?, ?)
|
|
21349
|
+
order by
|
|
21350
|
+
case
|
|
21351
|
+
when short_url = ? then 0
|
|
21352
|
+
when final_url = ? then 1
|
|
21353
|
+
else 2
|
|
21354
|
+
end
|
|
21355
|
+
limit 1
|
|
21356
|
+
`
|
|
21357
|
+
).get(
|
|
21358
|
+
shortUrl ?? url,
|
|
21359
|
+
url,
|
|
21360
|
+
shortUrl ?? url,
|
|
21361
|
+
url,
|
|
21362
|
+
shortUrl ?? url,
|
|
21363
|
+
url,
|
|
21364
|
+
shortUrl ?? url,
|
|
21365
|
+
url
|
|
21366
|
+
);
|
|
21367
|
+
}
|
|
21368
|
+
function hasUsefulPreview(row) {
|
|
21369
|
+
if (row.status !== "hit") return false;
|
|
21370
|
+
try {
|
|
21371
|
+
assertSafePreviewUrl(row.final_url || row.expanded_url || row.short_url);
|
|
21372
|
+
if (row.image_url) assertSafePreviewUrl(row.image_url);
|
|
21373
|
+
} catch {
|
|
21374
|
+
return false;
|
|
21375
|
+
}
|
|
21376
|
+
return Boolean(
|
|
21377
|
+
row.title || row.description || row.image_url || row.site_name
|
|
21378
|
+
);
|
|
21379
|
+
}
|
|
21380
|
+
function rowToPreview(row) {
|
|
21381
|
+
const url = assertSafePreviewUrl(
|
|
21382
|
+
row.final_url || row.expanded_url || row.short_url
|
|
21383
|
+
).toString();
|
|
21384
|
+
return {
|
|
21385
|
+
url,
|
|
21386
|
+
title: row.title ?? null,
|
|
21387
|
+
description: row.description ?? null,
|
|
21388
|
+
imageUrl: row.image_url ? assertSafePreviewUrl(row.image_url).toString() : null,
|
|
21389
|
+
siteName: row.site_name ?? null,
|
|
21390
|
+
...row.error ? { error: row.error } : {}
|
|
21391
|
+
};
|
|
21392
|
+
}
|
|
21393
|
+
function persistPreview(db, url, shortUrl, cached, preview) {
|
|
21394
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
21395
|
+
upsertUrlExpansion(
|
|
21396
|
+
db,
|
|
21397
|
+
normalizeUrlExpansionForIndex({
|
|
21398
|
+
url: cached?.short_url ?? shortUrl ?? url,
|
|
21399
|
+
expandedUrl: cached?.expanded_url ?? preview.url,
|
|
21400
|
+
finalUrl: preview.url,
|
|
21401
|
+
status: preview.error ? "error" : "hit",
|
|
21402
|
+
title: preview.title,
|
|
21403
|
+
description: preview.description,
|
|
21404
|
+
imageUrl: preview.imageUrl,
|
|
21405
|
+
siteName: preview.siteName,
|
|
21406
|
+
...preview.error ? { error: preview.error } : {},
|
|
21407
|
+
source: "metadata",
|
|
21408
|
+
updatedAt: now2
|
|
21409
|
+
})
|
|
21410
|
+
);
|
|
21411
|
+
}
|
|
21412
|
+
var getOrFetchLinkPreviewEffect = Effect37.fn("linkPreview.getOrFetch")(
|
|
21413
|
+
function* (url, options = {}) {
|
|
21414
|
+
const db = getNativeDb({ seedDemoData: false });
|
|
21415
|
+
const cached = readCachedPreview(db, url, options.shortUrl);
|
|
21416
|
+
if (cached && hasUsefulPreview(cached) && !options.refresh) {
|
|
21417
|
+
return rowToPreview(cached);
|
|
21418
|
+
}
|
|
21419
|
+
const preview = yield* fetchLinkPreviewMetadataEffect(
|
|
21420
|
+
cached?.final_url || cached?.expanded_url || url,
|
|
21421
|
+
options
|
|
21422
|
+
);
|
|
21423
|
+
persistPreview(db, url, options.shortUrl, cached, preview);
|
|
21424
|
+
return preview;
|
|
21425
|
+
}
|
|
21426
|
+
);
|
|
21237
21427
|
var __test__2 = {
|
|
21238
21428
|
...__test__,
|
|
21239
21429
|
decodeHtmlEntities,
|
|
@@ -21260,14 +21450,14 @@ function toError14(error) {
|
|
|
21260
21450
|
return error instanceof Error ? error : new Error(String(error));
|
|
21261
21451
|
}
|
|
21262
21452
|
function trySync10(try_) {
|
|
21263
|
-
return
|
|
21453
|
+
return Effect38.try({
|
|
21264
21454
|
try: try_,
|
|
21265
21455
|
catch: toError14
|
|
21266
21456
|
});
|
|
21267
21457
|
}
|
|
21268
21458
|
function cancelBodyEffect(response) {
|
|
21269
21459
|
return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(
|
|
21270
|
-
|
|
21460
|
+
Effect38.catchAll(() => Effect38.void)
|
|
21271
21461
|
);
|
|
21272
21462
|
}
|
|
21273
21463
|
function extractUrls(text) {
|
|
@@ -21303,7 +21493,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21303
21493
|
headers: { "user-agent": "birdclaw/0.3 url-expander" },
|
|
21304
21494
|
signal: AbortSignal.timeout(timeoutMs)
|
|
21305
21495
|
};
|
|
21306
|
-
return
|
|
21496
|
+
return Effect38.gen(function* () {
|
|
21307
21497
|
if (resolveHost) {
|
|
21308
21498
|
const headResponse = yield* safePreviewFetchEffect(url, {
|
|
21309
21499
|
...usesInjectedFetch ? { fetchImpl } : {},
|
|
@@ -21314,7 +21504,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21314
21504
|
yield* cancelBodyEffect(headResponse);
|
|
21315
21505
|
const headFinalUrl = headResponse.url || url;
|
|
21316
21506
|
if (headFinalUrl !== url && headResponse.status < 400) {
|
|
21317
|
-
yield*
|
|
21507
|
+
yield* Effect38.try({
|
|
21318
21508
|
try: () => assertSafePreviewUrl(headFinalUrl),
|
|
21319
21509
|
catch: (error) => error
|
|
21320
21510
|
});
|
|
@@ -21333,7 +21523,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21333
21523
|
yield* cancelBodyEffect(response2);
|
|
21334
21524
|
const finalUrl2 = response2.url || headFinalUrl;
|
|
21335
21525
|
if (finalUrl2 !== url) {
|
|
21336
|
-
yield*
|
|
21526
|
+
yield* Effect38.try({
|
|
21337
21527
|
try: () => assertSafePreviewUrl(finalUrl2),
|
|
21338
21528
|
catch: (error) => error
|
|
21339
21529
|
});
|
|
@@ -21348,7 +21538,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21348
21538
|
let currentUrl = url;
|
|
21349
21539
|
let response = null;
|
|
21350
21540
|
for (let redirect = 0; redirect <= MAX_REDIRECTS2; redirect += 1) {
|
|
21351
|
-
yield*
|
|
21541
|
+
yield* Effect38.try({
|
|
21352
21542
|
try: () => assertSafePreviewUrl(currentUrl),
|
|
21353
21543
|
catch: (error) => error
|
|
21354
21544
|
});
|
|
@@ -21362,11 +21552,11 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21362
21552
|
const location = response.headers.get("location");
|
|
21363
21553
|
if (!location) break;
|
|
21364
21554
|
if (redirect >= MAX_REDIRECTS2) {
|
|
21365
|
-
return yield*
|
|
21555
|
+
return yield* Effect38.fail(
|
|
21366
21556
|
new Error("URL expansion redirected too many times")
|
|
21367
21557
|
);
|
|
21368
21558
|
}
|
|
21369
|
-
currentUrl = yield*
|
|
21559
|
+
currentUrl = yield* Effect38.try({
|
|
21370
21560
|
try: () => new URL(location, currentUrl).toString(),
|
|
21371
21561
|
catch: (error) => error
|
|
21372
21562
|
});
|
|
@@ -21383,11 +21573,11 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21383
21573
|
const location = response.headers.get("location");
|
|
21384
21574
|
if (!location) break;
|
|
21385
21575
|
if (redirect >= MAX_REDIRECTS2) {
|
|
21386
|
-
return yield*
|
|
21576
|
+
return yield* Effect38.fail(
|
|
21387
21577
|
new Error("URL expansion redirected too many times")
|
|
21388
21578
|
);
|
|
21389
21579
|
}
|
|
21390
|
-
currentUrl = yield*
|
|
21580
|
+
currentUrl = yield* Effect38.try({
|
|
21391
21581
|
try: () => new URL(location, currentUrl).toString(),
|
|
21392
21582
|
catch: (error) => error
|
|
21393
21583
|
});
|
|
@@ -21397,14 +21587,14 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21397
21587
|
break;
|
|
21398
21588
|
}
|
|
21399
21589
|
if (!response) {
|
|
21400
|
-
return yield*
|
|
21590
|
+
return yield* Effect38.fail(new Error("URL expansion failed"));
|
|
21401
21591
|
}
|
|
21402
21592
|
if (response.status >= 300 && response.status < 400) {
|
|
21403
|
-
return yield*
|
|
21593
|
+
return yield* Effect38.fail(new Error("URL expansion ended on a redirect"));
|
|
21404
21594
|
}
|
|
21405
21595
|
const finalUrl = response.url || currentUrl;
|
|
21406
21596
|
if (finalUrl !== url) {
|
|
21407
|
-
yield*
|
|
21597
|
+
yield* Effect38.try({
|
|
21408
21598
|
try: () => assertSafePreviewUrl(finalUrl),
|
|
21409
21599
|
catch: (error) => error
|
|
21410
21600
|
});
|
|
@@ -21416,8 +21606,8 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21416
21606
|
...response.ok ? {} : { error: `HTTP ${response.status}` }
|
|
21417
21607
|
};
|
|
21418
21608
|
}).pipe(
|
|
21419
|
-
|
|
21420
|
-
(error) =>
|
|
21609
|
+
Effect38.catchAll(
|
|
21610
|
+
(error) => Effect38.succeed({
|
|
21421
21611
|
expandedUrl: url,
|
|
21422
21612
|
finalUrl: url,
|
|
21423
21613
|
status: "error",
|
|
@@ -21427,7 +21617,7 @@ function fetchExpansionEffect(url, fetchImpl, usesInjectedFetch, timeoutMs, reso
|
|
|
21427
21617
|
);
|
|
21428
21618
|
}
|
|
21429
21619
|
function expandUrlsEffect(urls, options = {}) {
|
|
21430
|
-
return
|
|
21620
|
+
return Effect38.gen(function* () {
|
|
21431
21621
|
const uniqueUrls = Array.from(new Set(urls));
|
|
21432
21622
|
const usesInjectedFetch = options.fetchImpl !== void 0;
|
|
21433
21623
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
@@ -21654,7 +21844,7 @@ function registerDirectMessageCommands({
|
|
|
21654
21844
|
|
|
21655
21845
|
// src/lib/follow-graph.ts
|
|
21656
21846
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
21657
|
-
import { Effect as
|
|
21847
|
+
import { Effect as Effect39 } from "effect";
|
|
21658
21848
|
var DEFAULT_FOLLOW_CACHE_TTL_MS = 24 * 60 * 6e4;
|
|
21659
21849
|
var DEFAULT_FOLLOW_PAGE_LIMIT = 1e3;
|
|
21660
21850
|
var MIN_FOLLOW_PAGE_LIMIT = 1;
|
|
@@ -21796,7 +21986,7 @@ function fetchFollowGraphViaXurlEffect({
|
|
|
21796
21986
|
maxPages,
|
|
21797
21987
|
maxResources
|
|
21798
21988
|
}) {
|
|
21799
|
-
return
|
|
21989
|
+
return Effect39.gen(function* () {
|
|
21800
21990
|
const result = yield* runSyncPlanEffect({
|
|
21801
21991
|
fetchPage: ({ cursor }) => liveTransportGateway.xurl.listFollowUsers({
|
|
21802
21992
|
direction,
|
|
@@ -21820,7 +22010,7 @@ function fetchFollowGraphViaBirdEffect({
|
|
|
21820
22010
|
maxPages,
|
|
21821
22011
|
maxResources
|
|
21822
22012
|
}) {
|
|
21823
|
-
return
|
|
22013
|
+
return Effect39.gen(function* () {
|
|
21824
22014
|
const birdLimit = Math.min(limit, BIRD_FOLLOW_PAGE_LIMIT);
|
|
21825
22015
|
const cappedMaxPages = maxResources === void 0 ? maxPages : Math.min(
|
|
21826
22016
|
maxPages ?? Number.POSITIVE_INFINITY,
|
|
@@ -21849,7 +22039,7 @@ function fetchFollowGraphEffect({
|
|
|
21849
22039
|
maxPages,
|
|
21850
22040
|
maxResources
|
|
21851
22041
|
}) {
|
|
21852
|
-
return
|
|
22042
|
+
return Effect39.gen(function* () {
|
|
21853
22043
|
if (mode === "bird") {
|
|
21854
22044
|
return {
|
|
21855
22045
|
source: "bird",
|
|
@@ -21882,8 +22072,8 @@ function fetchFollowGraphEffect({
|
|
|
21882
22072
|
maxPages,
|
|
21883
22073
|
maxResources
|
|
21884
22074
|
}).pipe(
|
|
21885
|
-
|
|
21886
|
-
|
|
22075
|
+
Effect39.map((payload) => ({ ok: true, payload })),
|
|
22076
|
+
Effect39.catchAll((error) => Effect39.succeed({ ok: false, error }))
|
|
21887
22077
|
);
|
|
21888
22078
|
if (birdResult.ok) {
|
|
21889
22079
|
return {
|
|
@@ -21899,8 +22089,8 @@ function fetchFollowGraphEffect({
|
|
|
21899
22089
|
maxPages,
|
|
21900
22090
|
maxResources
|
|
21901
22091
|
}).pipe(
|
|
21902
|
-
|
|
21903
|
-
|
|
22092
|
+
Effect39.map((payload) => ({ ok: true, payload })),
|
|
22093
|
+
Effect39.catchAll((error) => Effect39.succeed({ ok: false, error }))
|
|
21904
22094
|
);
|
|
21905
22095
|
if (xurlResult.ok) {
|
|
21906
22096
|
return {
|
|
@@ -21908,7 +22098,7 @@ function fetchFollowGraphEffect({
|
|
|
21908
22098
|
payload: xurlResult.payload
|
|
21909
22099
|
};
|
|
21910
22100
|
}
|
|
21911
|
-
return yield*
|
|
22101
|
+
return yield* Effect39.fail(
|
|
21912
22102
|
new Error(
|
|
21913
22103
|
`follow graph sync failed via bird and xurl: bird: ${errorMessage3(
|
|
21914
22104
|
birdResult.error
|
|
@@ -22098,7 +22288,7 @@ function errorMessage3(error) {
|
|
|
22098
22288
|
return error instanceof Error ? error.message : String(error);
|
|
22099
22289
|
}
|
|
22100
22290
|
function syncFollowGraphEffect(options) {
|
|
22101
|
-
return
|
|
22291
|
+
return Effect39.gen(function* () {
|
|
22102
22292
|
const db = yield* trySync(() => getNativeDb());
|
|
22103
22293
|
const mode = yield* trySync(() => parseMode3(options.mode));
|
|
22104
22294
|
const limit = yield* trySync(() => parseLimit(options.limit));
|
|
@@ -22545,10 +22735,10 @@ function registerGraphCommands({
|
|
|
22545
22735
|
}
|
|
22546
22736
|
|
|
22547
22737
|
// src/lib/inbox.ts
|
|
22548
|
-
import { Effect as
|
|
22738
|
+
import { Effect as Effect41 } from "effect";
|
|
22549
22739
|
|
|
22550
22740
|
// src/lib/openai.ts
|
|
22551
|
-
import { Effect as
|
|
22741
|
+
import { Effect as Effect40 } from "effect";
|
|
22552
22742
|
var getEnv = (name) => process.env[name];
|
|
22553
22743
|
function clampScore(value) {
|
|
22554
22744
|
return Math.max(0, Math.min(100, Math.round(value)));
|
|
@@ -22557,16 +22747,16 @@ function toError15(error) {
|
|
|
22557
22747
|
return error instanceof Error ? error : new Error(String(error));
|
|
22558
22748
|
}
|
|
22559
22749
|
function trySync11(try_) {
|
|
22560
|
-
return
|
|
22750
|
+
return Effect40.try({
|
|
22561
22751
|
try: try_,
|
|
22562
22752
|
catch: toError15
|
|
22563
22753
|
});
|
|
22564
22754
|
}
|
|
22565
22755
|
function scoreInboxItemWithOpenAIEffect(input) {
|
|
22566
|
-
return
|
|
22756
|
+
return Effect40.gen(function* () {
|
|
22567
22757
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
22568
22758
|
if (!apiKey) {
|
|
22569
|
-
return yield*
|
|
22759
|
+
return yield* Effect40.fail(new Error("OPENAI_API_KEY is not set"));
|
|
22570
22760
|
}
|
|
22571
22761
|
const model = process.env.BIRDCLAW_OPENAI_MODEL || "gpt-5.2";
|
|
22572
22762
|
const baseUrl = resolveOpenAIBaseUrl(getEnv);
|
|
@@ -22595,30 +22785,30 @@ function scoreInboxItemWithOpenAIEffect(input) {
|
|
|
22595
22785
|
})
|
|
22596
22786
|
})
|
|
22597
22787
|
).pipe(
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
(error) =>
|
|
22788
|
+
Effect40.mapError(toError15),
|
|
22789
|
+
Effect40.tapError(
|
|
22790
|
+
(error) => Effect40.sync(
|
|
22601
22791
|
() => debugLog(getEnv, `network error for ${url}: ${error.message}`)
|
|
22602
22792
|
)
|
|
22603
22793
|
)
|
|
22604
22794
|
);
|
|
22605
22795
|
if (!response.ok) {
|
|
22606
22796
|
const text = yield* tryPromise(() => response.text()).pipe(
|
|
22607
|
-
|
|
22797
|
+
Effect40.mapError(toError15)
|
|
22608
22798
|
);
|
|
22609
22799
|
debugLog(getEnv, `${url} -> ${response.status} ${text.slice(0, 400)}`);
|
|
22610
|
-
return yield*
|
|
22800
|
+
return yield* Effect40.fail(
|
|
22611
22801
|
new Error(
|
|
22612
22802
|
`OpenAI request failed: ${response.status} ${text.slice(0, 400)}`
|
|
22613
22803
|
)
|
|
22614
22804
|
);
|
|
22615
22805
|
}
|
|
22616
22806
|
const payload = yield* tryPromise(() => response.json()).pipe(
|
|
22617
|
-
|
|
22807
|
+
Effect40.mapError(toError15)
|
|
22618
22808
|
);
|
|
22619
22809
|
const content = payload.choices?.[0]?.message?.content;
|
|
22620
22810
|
if (!content) {
|
|
22621
|
-
return yield*
|
|
22811
|
+
return yield* Effect40.fail(new Error("OpenAI returned no content"));
|
|
22622
22812
|
}
|
|
22623
22813
|
const parsed = yield* trySync11(
|
|
22624
22814
|
() => JSON.parse(content)
|
|
@@ -22761,7 +22951,7 @@ function toError16(error) {
|
|
|
22761
22951
|
return error instanceof Error ? error : new Error(String(error));
|
|
22762
22952
|
}
|
|
22763
22953
|
function trySync12(try_) {
|
|
22764
|
-
return
|
|
22954
|
+
return Effect41.try({
|
|
22765
22955
|
try: try_,
|
|
22766
22956
|
catch: toError16
|
|
22767
22957
|
});
|
|
@@ -22771,7 +22961,7 @@ function scoreInboxEffect({
|
|
|
22771
22961
|
account,
|
|
22772
22962
|
limit = 8
|
|
22773
22963
|
} = {}) {
|
|
22774
|
-
return
|
|
22964
|
+
return Effect41.gen(function* () {
|
|
22775
22965
|
const db = yield* trySync12(() => getNativeDb());
|
|
22776
22966
|
const items = yield* trySync12(
|
|
22777
22967
|
() => listInboxItems({ kind, account, limit }).items
|
|
@@ -22867,7 +23057,7 @@ import path9 from "node:path";
|
|
|
22867
23057
|
import fs3 from "node:fs/promises";
|
|
22868
23058
|
import os2 from "node:os";
|
|
22869
23059
|
import path7 from "node:path";
|
|
22870
|
-
import { Effect as
|
|
23060
|
+
import { Effect as Effect42 } from "effect";
|
|
22871
23061
|
var DEFAULT_LAUNCHD_PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
22872
23062
|
function expandHomePath(input) {
|
|
22873
23063
|
return input === "~" || input.startsWith("~/") ? path7.join(os2.homedir(), input.slice(2)) : input;
|
|
@@ -22955,7 +23145,7 @@ function buildLaunchAgent({
|
|
|
22955
23145
|
};
|
|
22956
23146
|
}
|
|
22957
23147
|
function installLaunchAgentEffect(agent, options = {}) {
|
|
22958
|
-
return
|
|
23148
|
+
return Effect42.gen(function* () {
|
|
22959
23149
|
const launchAgentsDir = resolveUserPath(
|
|
22960
23150
|
options.launchAgentsDir ?? "~/Library/LaunchAgents"
|
|
22961
23151
|
);
|
|
@@ -22974,7 +23164,7 @@ function installLaunchAgentEffect(agent, options = {}) {
|
|
|
22974
23164
|
yield* runSubprocessEffect({
|
|
22975
23165
|
command: "launchctl",
|
|
22976
23166
|
args: ["unload", plistPath]
|
|
22977
|
-
}).pipe(
|
|
23167
|
+
}).pipe(Effect42.catchAll(() => Effect42.void));
|
|
22978
23168
|
yield* runSubprocessEffect({
|
|
22979
23169
|
command: "launchctl",
|
|
22980
23170
|
args: ["load", "-w", plistPath]
|
|
@@ -23003,7 +23193,7 @@ function installLaunchAgent(agent, options = {}) {
|
|
|
23003
23193
|
import fs4 from "node:fs/promises";
|
|
23004
23194
|
import os3 from "node:os";
|
|
23005
23195
|
import path8 from "node:path";
|
|
23006
|
-
import { Effect as
|
|
23196
|
+
import { Effect as Effect43 } from "effect";
|
|
23007
23197
|
function isFileExistsError(error) {
|
|
23008
23198
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
23009
23199
|
}
|
|
@@ -23061,17 +23251,17 @@ async function acquireScheduledJobLock(lockPath, staleMs) {
|
|
|
23061
23251
|
}
|
|
23062
23252
|
function acquireScheduledJobLockEffect(lockPath, staleMs) {
|
|
23063
23253
|
return tryPromise(() => acquireScheduledJobLock(lockPath, staleMs)).pipe(
|
|
23064
|
-
|
|
23254
|
+
Effect43.map(
|
|
23065
23255
|
(release) => release ? () => tryPromise(release).pipe(
|
|
23066
|
-
|
|
23067
|
-
|
|
23256
|
+
Effect43.asVoid,
|
|
23257
|
+
Effect43.catchAll(() => Effect43.void)
|
|
23068
23258
|
) : void 0
|
|
23069
23259
|
)
|
|
23070
23260
|
);
|
|
23071
23261
|
}
|
|
23072
23262
|
|
|
23073
23263
|
// src/lib/timeline-collections-live.ts
|
|
23074
|
-
import { Effect as
|
|
23264
|
+
import { Effect as Effect44 } from "effect";
|
|
23075
23265
|
var DEFAULT_COLLECTION_CACHE_TTL_MS = 2 * 6e4;
|
|
23076
23266
|
var DEFAULT_EARLY_STOP_MAX_PAGES = 10;
|
|
23077
23267
|
var MIN_XURL_LIMIT = 5;
|
|
@@ -23194,12 +23384,12 @@ function fetchXurlCollectionEffect({
|
|
|
23194
23384
|
maxPages,
|
|
23195
23385
|
earlyStop
|
|
23196
23386
|
}) {
|
|
23197
|
-
return
|
|
23387
|
+
return Effect44.gen(function* () {
|
|
23198
23388
|
let resolvedUserId = userId;
|
|
23199
23389
|
if (!resolvedUserId) {
|
|
23200
23390
|
const [accountUser] = yield* liveTransportGateway.xurl.lookupUsersByHandles([username]);
|
|
23201
23391
|
if (!accountUser?.id) {
|
|
23202
|
-
return yield*
|
|
23392
|
+
return yield* Effect44.fail(
|
|
23203
23393
|
new Error(`Could not resolve Twitter user id for @${username}`)
|
|
23204
23394
|
);
|
|
23205
23395
|
}
|
|
@@ -23207,7 +23397,7 @@ function fetchXurlCollectionEffect({
|
|
|
23207
23397
|
}
|
|
23208
23398
|
let saturatedAtPage;
|
|
23209
23399
|
const result = yield* runSyncPlanEffect({
|
|
23210
|
-
fetchPage: ({ cursor, pageIndex }) =>
|
|
23400
|
+
fetchPage: ({ cursor, pageIndex }) => Effect44.gen(function* () {
|
|
23211
23401
|
const payload = yield* kind === "likes" ? liveTransportGateway.xurl.listLikes({
|
|
23212
23402
|
maxResults: limit,
|
|
23213
23403
|
username,
|
|
@@ -23289,7 +23479,7 @@ function syncTimelineCollectionEffect({
|
|
|
23289
23479
|
cacheTtlMs,
|
|
23290
23480
|
earlyStop = false
|
|
23291
23481
|
}) {
|
|
23292
|
-
return
|
|
23482
|
+
return Effect44.gen(function* () {
|
|
23293
23483
|
yield* trySync(() => assertLimit2(limit));
|
|
23294
23484
|
const parsedMaxPages = yield* trySync(() => parseMaxPages3(maxPages));
|
|
23295
23485
|
const shouldApplyEarlyStopCap = earlyStop && !all && parsedMaxPages === null && mode !== "bird";
|
|
@@ -23718,7 +23908,7 @@ function parseAccountSyncSteps(value) {
|
|
|
23718
23908
|
|
|
23719
23909
|
// src/lib/bookmark-sync-job.ts
|
|
23720
23910
|
import path10 from "node:path";
|
|
23721
|
-
import { Effect as
|
|
23911
|
+
import { Effect as Effect45 } from "effect";
|
|
23722
23912
|
var DEFAULT_BOOKMARK_SYNC_INTERVAL_SECONDS = 3 * 60 * 60;
|
|
23723
23913
|
var DEFAULT_BOOKMARK_SYNC_LIMIT = 100;
|
|
23724
23914
|
var DEFAULT_BOOKMARK_SYNC_MAX_PAGES = 5;
|
|
@@ -23738,7 +23928,7 @@ function toError17(error) {
|
|
|
23738
23928
|
return error instanceof Error ? error : new Error(String(error));
|
|
23739
23929
|
}
|
|
23740
23930
|
function trySync13(try_) {
|
|
23741
|
-
return
|
|
23931
|
+
return Effect45.try({
|
|
23742
23932
|
try: try_,
|
|
23743
23933
|
catch: toError17
|
|
23744
23934
|
});
|
|
@@ -23758,7 +23948,7 @@ function runBookmarkSyncJobEffect({
|
|
|
23758
23948
|
lockPath,
|
|
23759
23949
|
db
|
|
23760
23950
|
} = {}) {
|
|
23761
|
-
return
|
|
23951
|
+
return Effect45.gen(function* () {
|
|
23762
23952
|
yield* trySync13(() => ensureBirdclawDirs());
|
|
23763
23953
|
const database = db ?? (yield* trySync13(() => getNativeDb({ seedDemoData: false })));
|
|
23764
23954
|
const resolvedLogPath = yield* trySync13(
|
|
@@ -23799,8 +23989,8 @@ function runBookmarkSyncJobEffect({
|
|
|
23799
23989
|
yield* appendScheduledJobAuditEffect(resolvedLogPath, entry);
|
|
23800
23990
|
return entry;
|
|
23801
23991
|
}
|
|
23802
|
-
return yield*
|
|
23803
|
-
const result = yield*
|
|
23992
|
+
return yield* Effect45.gen(function* () {
|
|
23993
|
+
const result = yield* Effect45.gen(function* () {
|
|
23804
23994
|
const sync = yield* syncTimelineCollectionEffect({
|
|
23805
23995
|
kind: "bookmarks",
|
|
23806
23996
|
account,
|
|
@@ -23831,8 +24021,8 @@ function runBookmarkSyncJobEffect({
|
|
|
23831
24021
|
backup
|
|
23832
24022
|
};
|
|
23833
24023
|
}).pipe(
|
|
23834
|
-
|
|
23835
|
-
(error) =>
|
|
24024
|
+
Effect45.catchAll(
|
|
24025
|
+
(error) => Effect45.gen(function* () {
|
|
23836
24026
|
const after = yield* trySync13(() => ({
|
|
23837
24027
|
bookmarks: countBookmarks(database)
|
|
23838
24028
|
}));
|
|
@@ -23851,7 +24041,7 @@ function runBookmarkSyncJobEffect({
|
|
|
23851
24041
|
);
|
|
23852
24042
|
yield* appendScheduledJobAuditEffect(resolvedLogPath, result);
|
|
23853
24043
|
return result;
|
|
23854
|
-
}).pipe(
|
|
24044
|
+
}).pipe(Effect45.ensuring(releaseLock()));
|
|
23855
24045
|
});
|
|
23856
24046
|
}
|
|
23857
24047
|
function runBookmarkSyncJob(options = {}) {
|
|
@@ -23917,7 +24107,7 @@ function buildBookmarkSyncLaunchAgentPlist(options = {}) {
|
|
|
23917
24107
|
});
|
|
23918
24108
|
}
|
|
23919
24109
|
function installBookmarkSyncLaunchAgentEffect(options = {}) {
|
|
23920
|
-
return
|
|
24110
|
+
return Effect45.gen(function* () {
|
|
23921
24111
|
yield* trySync13(() => ensureBirdclawDirs());
|
|
23922
24112
|
const agent = yield* trySync13(
|
|
23923
24113
|
() => buildBookmarkSyncLaunchAgentPlist(options)
|
|
@@ -24020,7 +24210,7 @@ function registerJobCommands({ program: program2, print: print2 }) {
|
|
|
24020
24210
|
}
|
|
24021
24211
|
|
|
24022
24212
|
// src/lib/x-lists.ts
|
|
24023
|
-
import { Effect as
|
|
24213
|
+
import { Effect as Effect46 } from "effect";
|
|
24024
24214
|
var DEFAULT_MAX_LISTS = 20;
|
|
24025
24215
|
var DEFAULT_MEMBER_LIMIT = 20;
|
|
24026
24216
|
var DEFAULT_MAX_MEMBER_PAGES = 1;
|
|
@@ -24068,7 +24258,7 @@ function fetchOwnedListsEffect({
|
|
|
24068
24258
|
maxLists
|
|
24069
24259
|
}) {
|
|
24070
24260
|
if (source === "bird") {
|
|
24071
|
-
return
|
|
24261
|
+
return Effect46.gen(function* () {
|
|
24072
24262
|
const authenticated = yield* getAuthenticatedBirdAccountEffect();
|
|
24073
24263
|
yield* trySync(
|
|
24074
24264
|
() => assertLiveAccountMatches({
|
|
@@ -24081,12 +24271,12 @@ function fetchOwnedListsEffect({
|
|
|
24081
24271
|
return yield* listOwnedXListsViaBirdEffect({ maxResults: maxLists });
|
|
24082
24272
|
});
|
|
24083
24273
|
}
|
|
24084
|
-
return
|
|
24274
|
+
return Effect46.gen(function* () {
|
|
24085
24275
|
const authenticated = yield* lookupAuthenticatedOAuth2UserEffect(
|
|
24086
24276
|
account.username
|
|
24087
24277
|
);
|
|
24088
24278
|
if (!authenticated) {
|
|
24089
|
-
return yield*
|
|
24279
|
+
return yield* Effect46.fail(
|
|
24090
24280
|
new Error("xurl did not return an OAuth2 user")
|
|
24091
24281
|
);
|
|
24092
24282
|
}
|
|
@@ -24102,7 +24292,7 @@ function fetchOwnedListsEffect({
|
|
|
24102
24292
|
);
|
|
24103
24293
|
const userId = account.externalUserId ?? liveExternalUserId;
|
|
24104
24294
|
if (!userId) {
|
|
24105
|
-
return yield*
|
|
24295
|
+
return yield* Effect46.fail(
|
|
24106
24296
|
new Error("xurl authenticated user is missing an id")
|
|
24107
24297
|
);
|
|
24108
24298
|
}
|
|
@@ -24127,7 +24317,7 @@ function fetchListMembersEffect({
|
|
|
24127
24317
|
maxPages: maxMemberPages
|
|
24128
24318
|
});
|
|
24129
24319
|
}
|
|
24130
|
-
return
|
|
24320
|
+
return Effect46.gen(function* () {
|
|
24131
24321
|
const data = [];
|
|
24132
24322
|
const seen = /* @__PURE__ */ new Set();
|
|
24133
24323
|
let paginationToken;
|
|
@@ -24280,7 +24470,7 @@ function persistListResult({
|
|
|
24280
24470
|
})();
|
|
24281
24471
|
}
|
|
24282
24472
|
function syncXListsEffect(options = {}) {
|
|
24283
|
-
return
|
|
24473
|
+
return Effect46.suspend(() => {
|
|
24284
24474
|
const mode = normalizedMode(options.mode);
|
|
24285
24475
|
const maxLists = positiveInteger(
|
|
24286
24476
|
"--max-lists",
|
|
@@ -24310,13 +24500,13 @@ function syncXListsEffect(options = {}) {
|
|
|
24310
24500
|
fetchOwnedListsEffect({ source, account, maxLists })
|
|
24311
24501
|
)
|
|
24312
24502
|
);
|
|
24313
|
-
return
|
|
24503
|
+
return Effect46.gen(function* () {
|
|
24314
24504
|
const discovered = yield* fetchWithTransportFallbackEffect(transports);
|
|
24315
24505
|
const source = discovered.source;
|
|
24316
24506
|
const lists = discovered.payload.data.slice(0, maxLists);
|
|
24317
24507
|
const results = [];
|
|
24318
24508
|
for (const [index, list] of lists.entries()) {
|
|
24319
|
-
if (index > 0 && delayMs > 0) yield*
|
|
24509
|
+
if (index > 0 && delayMs > 0) yield* Effect46.sleep(delayMs);
|
|
24320
24510
|
const fetched = yield* fetchListMembersEffect({
|
|
24321
24511
|
source,
|
|
24322
24512
|
list,
|
|
@@ -24324,9 +24514,9 @@ function syncXListsEffect(options = {}) {
|
|
|
24324
24514
|
memberLimit,
|
|
24325
24515
|
maxMemberPages
|
|
24326
24516
|
}).pipe(
|
|
24327
|
-
|
|
24328
|
-
|
|
24329
|
-
(error) =>
|
|
24517
|
+
Effect46.map((payload) => ({ ok: true, payload })),
|
|
24518
|
+
Effect46.catchAll(
|
|
24519
|
+
(error) => Effect46.succeed({ ok: false, error: toError(error) })
|
|
24330
24520
|
)
|
|
24331
24521
|
);
|
|
24332
24522
|
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -24551,23 +24741,23 @@ function registerListCommands({
|
|
|
24551
24741
|
}
|
|
24552
24742
|
|
|
24553
24743
|
// src/lib/profile-replies.ts
|
|
24554
|
-
import { Effect as
|
|
24744
|
+
import { Effect as Effect50 } from "effect";
|
|
24555
24745
|
|
|
24556
24746
|
// src/lib/moderation-target.ts
|
|
24557
|
-
import { Effect as
|
|
24747
|
+
import { Effect as Effect49 } from "effect";
|
|
24558
24748
|
|
|
24559
24749
|
// src/lib/bird-actions.ts
|
|
24560
|
-
import { Effect as
|
|
24750
|
+
import { Effect as Effect48 } from "effect";
|
|
24561
24751
|
|
|
24562
24752
|
// src/lib/bird-command.ts
|
|
24563
24753
|
import { access } from "node:fs/promises";
|
|
24564
24754
|
import { constants as constants2 } from "node:fs";
|
|
24565
|
-
import { Data as
|
|
24566
|
-
var BirdCommandUnavailableError = class extends
|
|
24755
|
+
import { Data as Data4, Effect as Effect47 } from "effect";
|
|
24756
|
+
var BirdCommandUnavailableError = class extends Data4.TaggedError(
|
|
24567
24757
|
"BirdCommandUnavailableError"
|
|
24568
24758
|
) {
|
|
24569
24759
|
};
|
|
24570
|
-
var BirdCommandExecutionError = class extends
|
|
24760
|
+
var BirdCommandExecutionError = class extends Data4.TaggedError(
|
|
24571
24761
|
"BirdCommandExecutionError"
|
|
24572
24762
|
) {
|
|
24573
24763
|
};
|
|
@@ -24615,25 +24805,25 @@ function execFailureFromCause(command, cause) {
|
|
|
24615
24805
|
}
|
|
24616
24806
|
function assertBirdCommandAvailableEffect(command) {
|
|
24617
24807
|
if (!isPathCommand(command)) {
|
|
24618
|
-
return
|
|
24808
|
+
return Effect47.void;
|
|
24619
24809
|
}
|
|
24620
|
-
return
|
|
24810
|
+
return Effect47.tryPromise({
|
|
24621
24811
|
try: () => Promise.resolve(access(command, constants2.X_OK)),
|
|
24622
24812
|
catch: (cause) => new BirdCommandUnavailableError({
|
|
24623
24813
|
message: formatBirdInstallHint(command),
|
|
24624
24814
|
command,
|
|
24625
24815
|
cause
|
|
24626
24816
|
})
|
|
24627
|
-
}).pipe(
|
|
24817
|
+
}).pipe(Effect47.asVoid);
|
|
24628
24818
|
}
|
|
24629
24819
|
function getBirdCommandEffect() {
|
|
24630
|
-
return
|
|
24820
|
+
return Effect47.try({
|
|
24631
24821
|
try: () => getBirdCommand(),
|
|
24632
24822
|
catch: (cause) => cause instanceof Error ? cause : new Error(String(cause))
|
|
24633
24823
|
});
|
|
24634
24824
|
}
|
|
24635
24825
|
function runBirdCommandEffect(args, options) {
|
|
24636
|
-
return
|
|
24826
|
+
return Effect47.gen(function* () {
|
|
24637
24827
|
const birdCommand = yield* getBirdCommandEffect();
|
|
24638
24828
|
yield* assertBirdCommandAvailableEffect(birdCommand);
|
|
24639
24829
|
return yield* runSubprocessEffect({
|
|
@@ -24646,8 +24836,8 @@ function runBirdCommandEffect(args, options) {
|
|
|
24646
24836
|
...options?.signal ? { signal: options.signal } : {},
|
|
24647
24837
|
...options?.killSignal ? { killSignal: options.killSignal } : {}
|
|
24648
24838
|
}).pipe(
|
|
24649
|
-
|
|
24650
|
-
|
|
24839
|
+
Effect47.map(({ stdout, stderr }) => ({ stdout, stderr })),
|
|
24840
|
+
Effect47.mapError((cause) => execFailureFromCause(birdCommand, cause))
|
|
24651
24841
|
);
|
|
24652
24842
|
});
|
|
24653
24843
|
}
|
|
@@ -24685,9 +24875,9 @@ function toError18(error) {
|
|
|
24685
24875
|
return error instanceof Error ? error : new Error(String(error));
|
|
24686
24876
|
}
|
|
24687
24877
|
function runBirdJsonCommandEffect2(args) {
|
|
24688
|
-
return
|
|
24878
|
+
return Effect48.gen(function* () {
|
|
24689
24879
|
const { stdout } = yield* runBirdCommandEffect(args);
|
|
24690
|
-
return yield*
|
|
24880
|
+
return yield* Effect48.try({
|
|
24691
24881
|
try: () => JSON.parse(stripAnsi2(stdout)),
|
|
24692
24882
|
catch: toError18
|
|
24693
24883
|
});
|
|
@@ -24701,16 +24891,13 @@ function safeBirdProfileArg(query) {
|
|
|
24701
24891
|
throw new Error("Invalid bird profile query");
|
|
24702
24892
|
}
|
|
24703
24893
|
function readBirdStatusViaBirdEffect(query) {
|
|
24704
|
-
return
|
|
24705
|
-
const safeQuery = yield*
|
|
24894
|
+
return Effect48.gen(function* () {
|
|
24895
|
+
const safeQuery = yield* Effect48.try({
|
|
24706
24896
|
try: () => safeBirdProfileArg(query),
|
|
24707
24897
|
catch: toError18
|
|
24708
24898
|
});
|
|
24709
24899
|
return yield* runBirdJsonCommandEffect2(["status", safeQuery, "--json"]);
|
|
24710
|
-
}).pipe(
|
|
24711
|
-
}
|
|
24712
|
-
function readBirdStatusViaBird(query) {
|
|
24713
|
-
return runEffectPromise(readBirdStatusViaBirdEffect(query));
|
|
24900
|
+
}).pipe(Effect48.catchAll(() => Effect48.succeed(null)));
|
|
24714
24901
|
}
|
|
24715
24902
|
function toBirdLookupUser(payload) {
|
|
24716
24903
|
const user = payload.user && typeof payload.user === "object" ? payload.user : null;
|
|
@@ -24744,8 +24931,8 @@ function toBirdLookupUser(payload) {
|
|
|
24744
24931
|
};
|
|
24745
24932
|
}
|
|
24746
24933
|
function lookupProfileViaBirdEffect2(query) {
|
|
24747
|
-
return
|
|
24748
|
-
const safeQuery = yield*
|
|
24934
|
+
return Effect48.gen(function* () {
|
|
24935
|
+
const safeQuery = yield* Effect48.try({
|
|
24749
24936
|
try: () => safeBirdProfileArg(query),
|
|
24750
24937
|
catch: toError18
|
|
24751
24938
|
});
|
|
@@ -24756,14 +24943,11 @@ function lookupProfileViaBirdEffect2(query) {
|
|
|
24756
24943
|
"1",
|
|
24757
24944
|
"--json"
|
|
24758
24945
|
]);
|
|
24759
|
-
return yield*
|
|
24946
|
+
return yield* Effect48.try({
|
|
24760
24947
|
try: () => toBirdLookupUser(payload),
|
|
24761
24948
|
catch: toError18
|
|
24762
24949
|
});
|
|
24763
|
-
}).pipe(
|
|
24764
|
-
}
|
|
24765
|
-
function lookupProfileViaBird(query) {
|
|
24766
|
-
return runEffectPromise(lookupProfileViaBirdEffect2(query));
|
|
24950
|
+
}).pipe(Effect48.catchAll(() => Effect48.succeed(null)));
|
|
24767
24951
|
}
|
|
24768
24952
|
function runVerifiedBirdMutationEffect({
|
|
24769
24953
|
action,
|
|
@@ -24771,8 +24955,8 @@ function runVerifiedBirdMutationEffect({
|
|
|
24771
24955
|
verifyField,
|
|
24772
24956
|
expectedValue
|
|
24773
24957
|
}) {
|
|
24774
|
-
return
|
|
24775
|
-
const safeQuery = yield*
|
|
24958
|
+
return Effect48.gen(function* () {
|
|
24959
|
+
const safeQuery = yield* Effect48.try({
|
|
24776
24960
|
try: () => safeBirdProfileArg(query),
|
|
24777
24961
|
catch: toError18
|
|
24778
24962
|
});
|
|
@@ -24786,12 +24970,12 @@ function runVerifiedBirdMutationEffect({
|
|
|
24786
24970
|
action,
|
|
24787
24971
|
safeQuery
|
|
24788
24972
|
]).pipe(
|
|
24789
|
-
|
|
24973
|
+
Effect48.map(({ stdout, stderr }) => ({
|
|
24790
24974
|
ok: true,
|
|
24791
24975
|
output: normalizeOutput(stdout, stderr)
|
|
24792
24976
|
})),
|
|
24793
|
-
|
|
24794
|
-
(error) =>
|
|
24977
|
+
Effect48.catchAll(
|
|
24978
|
+
(error) => Effect48.succeed({
|
|
24795
24979
|
ok: false,
|
|
24796
24980
|
output: formatExecError2(error, `bird ${action} failed`)
|
|
24797
24981
|
})
|
|
@@ -24828,9 +25012,6 @@ function blockUserViaBirdEffect(query) {
|
|
|
24828
25012
|
expectedValue: true
|
|
24829
25013
|
});
|
|
24830
25014
|
}
|
|
24831
|
-
function blockUserViaBird(query) {
|
|
24832
|
-
return runEffectPromise(blockUserViaBirdEffect(query));
|
|
24833
|
-
}
|
|
24834
25015
|
function unblockUserViaBirdEffect(query) {
|
|
24835
25016
|
return runVerifiedBirdMutationEffect({
|
|
24836
25017
|
action: "unblock",
|
|
@@ -24839,9 +25020,6 @@ function unblockUserViaBirdEffect(query) {
|
|
|
24839
25020
|
expectedValue: false
|
|
24840
25021
|
});
|
|
24841
25022
|
}
|
|
24842
|
-
function unblockUserViaBird(query) {
|
|
24843
|
-
return runEffectPromise(unblockUserViaBirdEffect(query));
|
|
24844
|
-
}
|
|
24845
25023
|
function muteUserViaBirdEffect(query) {
|
|
24846
25024
|
return runVerifiedBirdMutationEffect({
|
|
24847
25025
|
action: "mute",
|
|
@@ -24850,9 +25028,6 @@ function muteUserViaBirdEffect(query) {
|
|
|
24850
25028
|
expectedValue: true
|
|
24851
25029
|
});
|
|
24852
25030
|
}
|
|
24853
|
-
function muteUserViaBird(query) {
|
|
24854
|
-
return runEffectPromise(muteUserViaBirdEffect(query));
|
|
24855
|
-
}
|
|
24856
25031
|
function unmuteUserViaBirdEffect(query) {
|
|
24857
25032
|
return runVerifiedBirdMutationEffect({
|
|
24858
25033
|
action: "unmute",
|
|
@@ -24861,16 +25036,13 @@ function unmuteUserViaBirdEffect(query) {
|
|
|
24861
25036
|
expectedValue: false
|
|
24862
25037
|
});
|
|
24863
25038
|
}
|
|
24864
|
-
function unmuteUserViaBird(query) {
|
|
24865
|
-
return runEffectPromise(unmuteUserViaBirdEffect(query));
|
|
24866
|
-
}
|
|
24867
25039
|
|
|
24868
25040
|
// src/lib/moderation-target.ts
|
|
24869
25041
|
function toError19(error) {
|
|
24870
25042
|
return error instanceof Error ? error : new Error(String(error));
|
|
24871
25043
|
}
|
|
24872
25044
|
function trySync14(try_) {
|
|
24873
|
-
return
|
|
25045
|
+
return Effect49.try({
|
|
24874
25046
|
try: try_,
|
|
24875
25047
|
catch: toError19
|
|
24876
25048
|
});
|
|
@@ -24918,11 +25090,11 @@ function resolveLocalProfile(db, normalizedQuery) {
|
|
|
24918
25090
|
};
|
|
24919
25091
|
}
|
|
24920
25092
|
function resolveProfileEffect(query, providedDb) {
|
|
24921
|
-
return
|
|
25093
|
+
return Effect49.gen(function* () {
|
|
24922
25094
|
const db = providedDb ?? (yield* trySync14(() => getNativeDb()));
|
|
24923
25095
|
const normalizedQuery = normalizeProfileQuery(query);
|
|
24924
25096
|
if (!normalizedQuery) {
|
|
24925
|
-
return yield*
|
|
25097
|
+
return yield* Effect49.fail(new Error("Missing profile handle or id"));
|
|
24926
25098
|
}
|
|
24927
25099
|
const local = yield* trySync14(
|
|
24928
25100
|
() => resolveLocalProfile(db, normalizedQuery)
|
|
@@ -24931,7 +25103,7 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
24931
25103
|
if (local) {
|
|
24932
25104
|
return local;
|
|
24933
25105
|
}
|
|
24934
|
-
return yield*
|
|
25106
|
+
return yield* Effect49.fail(
|
|
24935
25107
|
new Error(`Profile not found locally: ${query}`)
|
|
24936
25108
|
);
|
|
24937
25109
|
}
|
|
@@ -24940,11 +25112,11 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
24940
25112
|
}
|
|
24941
25113
|
let user;
|
|
24942
25114
|
let lastError;
|
|
24943
|
-
const birdResult = yield*
|
|
24944
|
-
|
|
25115
|
+
const birdResult = yield* lookupProfileViaBirdEffect2(
|
|
25116
|
+
local?.profile.handle ?? normalizedQuery
|
|
24945
25117
|
).pipe(
|
|
24946
|
-
|
|
24947
|
-
|
|
25118
|
+
Effect49.map((value) => ({ ok: true, value })),
|
|
25119
|
+
Effect49.catchAll((error) => Effect49.succeed({ ok: false, error }))
|
|
24948
25120
|
);
|
|
24949
25121
|
if (birdResult.ok) {
|
|
24950
25122
|
user = birdResult.value ?? void 0;
|
|
@@ -24952,12 +25124,12 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
24952
25124
|
lastError = birdResult.error;
|
|
24953
25125
|
}
|
|
24954
25126
|
if (!user) {
|
|
24955
|
-
const xurlResult = yield*
|
|
24956
|
-
|
|
24957
|
-
).pipe(
|
|
24958
|
-
|
|
24959
|
-
|
|
24960
|
-
(error) =>
|
|
25127
|
+
const xurlResult = yield* (/^\d+$/.test(normalizedQuery) ? lookupUsersByIdsEffect([normalizedQuery]) : lookupUsersByHandlesEffect([
|
|
25128
|
+
local?.profile.handle ?? normalizedQuery
|
|
25129
|
+
])).pipe(
|
|
25130
|
+
Effect49.map((value) => ({ ok: true, value })),
|
|
25131
|
+
Effect49.catchAll(
|
|
25132
|
+
(error) => Effect49.succeed({ ok: false, error })
|
|
24961
25133
|
)
|
|
24962
25134
|
);
|
|
24963
25135
|
if (xurlResult.ok) {
|
|
@@ -24970,13 +25142,13 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
24970
25142
|
if (local) {
|
|
24971
25143
|
return local;
|
|
24972
25144
|
}
|
|
24973
|
-
return yield*
|
|
25145
|
+
return yield* Effect49.fail(lastError);
|
|
24974
25146
|
}
|
|
24975
25147
|
if (!user) {
|
|
24976
25148
|
if (local) {
|
|
24977
25149
|
return local;
|
|
24978
25150
|
}
|
|
24979
|
-
return yield*
|
|
25151
|
+
return yield* Effect49.fail(new Error(`Profile not found: ${query}`));
|
|
24980
25152
|
}
|
|
24981
25153
|
if (local) {
|
|
24982
25154
|
return yield* databaseWriteEffect(
|
|
@@ -25017,11 +25189,11 @@ function inspectProfileReplies(query, { limit = 12 } = {}) {
|
|
|
25017
25189
|
return runEffectPromise(inspectProfileRepliesEffect(query, { limit }));
|
|
25018
25190
|
}
|
|
25019
25191
|
function inspectProfileRepliesEffect(query, { limit = 12 } = {}) {
|
|
25020
|
-
return
|
|
25192
|
+
return Effect50.gen(function* () {
|
|
25021
25193
|
const resolved = yield* tryPromise(() => resolveProfile(query));
|
|
25022
25194
|
const externalUserId = resolved.externalUserId;
|
|
25023
25195
|
if (!externalUserId) {
|
|
25024
|
-
return yield*
|
|
25196
|
+
return yield* Effect50.fail(
|
|
25025
25197
|
new Error(`Profile has no external Twitter user id: ${query}`)
|
|
25026
25198
|
);
|
|
25027
25199
|
}
|
|
@@ -25116,18 +25288,18 @@ function registerMentionCommands({
|
|
|
25116
25288
|
}
|
|
25117
25289
|
|
|
25118
25290
|
// src/lib/blocks.ts
|
|
25119
|
-
import { Effect as
|
|
25291
|
+
import { Effect as Effect53 } from "effect";
|
|
25120
25292
|
|
|
25121
25293
|
// src/lib/moderation-state.ts
|
|
25122
|
-
import { Effect as
|
|
25294
|
+
import { Effect as Effect52 } from "effect";
|
|
25123
25295
|
|
|
25124
25296
|
// src/lib/actions-transport.ts
|
|
25125
|
-
import { Effect as
|
|
25297
|
+
import { Effect as Effect51 } from "effect";
|
|
25126
25298
|
function toError20(error) {
|
|
25127
25299
|
return error instanceof Error ? error : new Error(String(error));
|
|
25128
25300
|
}
|
|
25129
25301
|
function trySync15(try_) {
|
|
25130
|
-
return
|
|
25302
|
+
return Effect51.try({
|
|
25131
25303
|
try: try_,
|
|
25132
25304
|
catch: toError20
|
|
25133
25305
|
});
|
|
@@ -25139,17 +25311,17 @@ function liveWritesDisabled4() {
|
|
|
25139
25311
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
25140
25312
|
}
|
|
25141
25313
|
function verifyExpectedAccountEffect(expectedAccount) {
|
|
25142
|
-
return
|
|
25314
|
+
return Effect51.gen(function* () {
|
|
25143
25315
|
if (!expectedAccount) return null;
|
|
25144
25316
|
if (liveWritesDisabled4()) return null;
|
|
25145
|
-
const sourceUser = yield*
|
|
25317
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
25146
25318
|
const sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
25147
25319
|
const sourceUsername = sourceUser && typeof sourceUser.username === "string" ? profileHandleKey(sourceUser.username) : "";
|
|
25148
25320
|
const expectedExternalUserId = expectedAccount.externalUserId?.trim() ?? "";
|
|
25149
25321
|
const expectedHandle = profileHandleKey(expectedAccount.handle);
|
|
25150
25322
|
if (expectedExternalUserId) {
|
|
25151
25323
|
if (sourceUserId === expectedExternalUserId) return sourceUserId;
|
|
25152
|
-
return yield*
|
|
25324
|
+
return yield* Effect51.fail(
|
|
25153
25325
|
new Error(
|
|
25154
25326
|
`xurl is authenticated as user ${sourceUserId || "unknown"}, not account ${expectedAccount.id}`
|
|
25155
25327
|
)
|
|
@@ -25157,7 +25329,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
25157
25329
|
}
|
|
25158
25330
|
if (expectedHandle && sourceUsername === expectedHandle)
|
|
25159
25331
|
return sourceUserId;
|
|
25160
|
-
return yield*
|
|
25332
|
+
return yield* Effect51.fail(
|
|
25161
25333
|
new Error(
|
|
25162
25334
|
sourceUsername ? `xurl is authenticated as @${sourceUsername}, not @${expectedHandle}` : "xurl authenticated user unavailable"
|
|
25163
25335
|
)
|
|
@@ -25165,10 +25337,8 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
25165
25337
|
});
|
|
25166
25338
|
}
|
|
25167
25339
|
function runBirdActionEffect(action, query) {
|
|
25168
|
-
return
|
|
25169
|
-
const result = yield*
|
|
25170
|
-
() => action === "block" ? blockUserViaBird(query) : action === "unblock" ? unblockUserViaBird(query) : action === "mute" ? muteUserViaBird(query) : unmuteUserViaBird(query)
|
|
25171
|
-
);
|
|
25340
|
+
return Effect51.gen(function* () {
|
|
25341
|
+
const result = yield* action === "block" ? blockUserViaBirdEffect(query) : action === "unblock" ? unblockUserViaBirdEffect(query) : action === "mute" ? muteUserViaBirdEffect(query) : unmuteUserViaBirdEffect(query);
|
|
25172
25342
|
return {
|
|
25173
25343
|
...result,
|
|
25174
25344
|
transport: "bird"
|
|
@@ -25185,7 +25355,7 @@ function getVerifyExpectation(action) {
|
|
|
25185
25355
|
};
|
|
25186
25356
|
}
|
|
25187
25357
|
function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId) {
|
|
25188
|
-
return
|
|
25358
|
+
return Effect51.gen(function* () {
|
|
25189
25359
|
if (!targetUserId) {
|
|
25190
25360
|
return {
|
|
25191
25361
|
ok: false,
|
|
@@ -25195,9 +25365,7 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
25195
25365
|
}
|
|
25196
25366
|
let sourceUserId = verifiedSourceUserId ?? "";
|
|
25197
25367
|
if (!sourceUserId) {
|
|
25198
|
-
const sourceUser = yield*
|
|
25199
|
-
() => lookupAuthenticatedUserFresh()
|
|
25200
|
-
);
|
|
25368
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
25201
25369
|
sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
25202
25370
|
}
|
|
25203
25371
|
if (!sourceUserId) {
|
|
@@ -25207,16 +25375,14 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
25207
25375
|
transport: "xurl"
|
|
25208
25376
|
};
|
|
25209
25377
|
}
|
|
25210
|
-
const result = yield*
|
|
25211
|
-
() => action === "block" ? blockUserViaXurl(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurl(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurl(sourceUserId, targetUserId) : unmuteUserViaXurl(sourceUserId, targetUserId)
|
|
25212
|
-
);
|
|
25378
|
+
const result = yield* action === "block" ? blockUserViaXurlEffect(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurlEffect(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurlEffect(sourceUserId, targetUserId) : unmuteUserViaXurlEffect(sourceUserId, targetUserId);
|
|
25213
25379
|
if (!result.ok) {
|
|
25214
25380
|
return {
|
|
25215
25381
|
...result,
|
|
25216
25382
|
transport: "xurl"
|
|
25217
25383
|
};
|
|
25218
25384
|
}
|
|
25219
|
-
const status = yield*
|
|
25385
|
+
const status = yield* readBirdStatusViaBirdEffect(query);
|
|
25220
25386
|
const { field: verifyField, expected: expectedValue } = getVerifyExpectation(action);
|
|
25221
25387
|
const actualValue = status && typeof status[verifyField] === "boolean" ? Boolean(status[verifyField]) : null;
|
|
25222
25388
|
if (actualValue === null) {
|
|
@@ -25250,13 +25416,13 @@ function runModerationActionEffect({
|
|
|
25250
25416
|
transport,
|
|
25251
25417
|
expectedAccount
|
|
25252
25418
|
}) {
|
|
25253
|
-
return
|
|
25419
|
+
return Effect51.gen(function* () {
|
|
25254
25420
|
const requestedTransport = yield* trySync15(
|
|
25255
25421
|
() => resolveActionsTransport(transport)
|
|
25256
25422
|
);
|
|
25257
25423
|
const verifyXurlAccount = () => verifyExpectedAccountEffect(expectedAccount).pipe(
|
|
25258
|
-
|
|
25259
|
-
(error) =>
|
|
25424
|
+
Effect51.catchAll(
|
|
25425
|
+
(error) => Effect51.succeed({
|
|
25260
25426
|
ok: false,
|
|
25261
25427
|
output: error instanceof Error ? error.message : String(error),
|
|
25262
25428
|
transport: "xurl"
|
|
@@ -25345,7 +25511,7 @@ function toError21(error) {
|
|
|
25345
25511
|
return error instanceof Error ? error : new Error(String(error));
|
|
25346
25512
|
}
|
|
25347
25513
|
function trySync16(try_) {
|
|
25348
|
-
return
|
|
25514
|
+
return Effect52.try({
|
|
25349
25515
|
try: try_,
|
|
25350
25516
|
catch: toError21
|
|
25351
25517
|
});
|
|
@@ -25464,20 +25630,20 @@ function resolveModerationTargetEffect({
|
|
|
25464
25630
|
query,
|
|
25465
25631
|
selfActionError
|
|
25466
25632
|
}) {
|
|
25467
|
-
return
|
|
25633
|
+
return Effect52.gen(function* () {
|
|
25468
25634
|
const db = yield* trySync16(() => getNativeDb());
|
|
25469
25635
|
const resolvedAccountId = accountId || getDefaultAccountId(db);
|
|
25470
25636
|
const accountHandle = yield* trySync16(
|
|
25471
25637
|
() => getAccountHandle(db, resolvedAccountId)
|
|
25472
25638
|
);
|
|
25473
25639
|
if (!accountHandle) {
|
|
25474
|
-
return yield*
|
|
25640
|
+
return yield* Effect52.fail(
|
|
25475
25641
|
new Error(`Unknown account: ${resolvedAccountId}`)
|
|
25476
25642
|
);
|
|
25477
25643
|
}
|
|
25478
25644
|
const normalizedQuery = normalizeProfileQuery(query);
|
|
25479
25645
|
if (normalizedQuery.toLowerCase() === accountHandle.toLowerCase()) {
|
|
25480
|
-
return yield*
|
|
25646
|
+
return yield* Effect52.fail(new Error(selfActionError));
|
|
25481
25647
|
}
|
|
25482
25648
|
const resolved = yield* resolveProfileEffect(query, db);
|
|
25483
25649
|
const account = yield* trySync16(
|
|
@@ -25488,7 +25654,7 @@ function resolveModerationTargetEffect({
|
|
|
25488
25654
|
);
|
|
25489
25655
|
const accountExternalUserId = account?.external_user_id ?? getExternalUserId(accountProfile?.id ?? "");
|
|
25490
25656
|
if (profileHandleKey(resolved.profile.handle) === profileHandleKey(accountHandle) || accountProfile?.id && resolved.profile.id === accountProfile.id || accountExternalUserId && resolved.externalUserId === accountExternalUserId) {
|
|
25491
|
-
return yield*
|
|
25657
|
+
return yield* Effect52.fail(new Error(selfActionError));
|
|
25492
25658
|
}
|
|
25493
25659
|
return {
|
|
25494
25660
|
db,
|
|
@@ -25505,7 +25671,7 @@ function resolveModerationTargetEffect({
|
|
|
25505
25671
|
}
|
|
25506
25672
|
function addModerationEffect(kind, accountId, query, options = {}) {
|
|
25507
25673
|
const descriptor = moderationDescriptors[kind];
|
|
25508
|
-
return
|
|
25674
|
+
return Effect52.gen(function* () {
|
|
25509
25675
|
const { db, resolved, resolvedAccountId, accountIdentity, actionQuery } = yield* resolveModerationTargetEffect({
|
|
25510
25676
|
accountId,
|
|
25511
25677
|
query,
|
|
@@ -25555,7 +25721,7 @@ function addModeration(kind, accountId, query, options = {}) {
|
|
|
25555
25721
|
}
|
|
25556
25722
|
function recordModerationEffect(kind, accountId, query) {
|
|
25557
25723
|
const descriptor = moderationDescriptors[kind];
|
|
25558
|
-
return
|
|
25724
|
+
return Effect52.gen(function* () {
|
|
25559
25725
|
const { db, resolved, resolvedAccountId } = yield* resolveModerationTargetEffect({
|
|
25560
25726
|
accountId,
|
|
25561
25727
|
query,
|
|
@@ -25586,7 +25752,7 @@ function recordModeration(kind, accountId, query) {
|
|
|
25586
25752
|
}
|
|
25587
25753
|
function removeModerationEffect(kind, accountId, query, options = {}) {
|
|
25588
25754
|
const descriptor = moderationDescriptors[kind];
|
|
25589
|
-
return
|
|
25755
|
+
return Effect52.gen(function* () {
|
|
25590
25756
|
const { db, resolved, resolvedAccountId, accountIdentity, actionQuery } = yield* resolveModerationTargetEffect({
|
|
25591
25757
|
accountId,
|
|
25592
25758
|
query,
|
|
@@ -25662,7 +25828,7 @@ function toError22(error) {
|
|
|
25662
25828
|
return error instanceof Error ? error : new Error(String(error));
|
|
25663
25829
|
}
|
|
25664
25830
|
function trySync17(try_) {
|
|
25665
|
-
return
|
|
25831
|
+
return Effect53.try({
|
|
25666
25832
|
try: try_,
|
|
25667
25833
|
catch: toError22
|
|
25668
25834
|
});
|
|
@@ -25675,12 +25841,12 @@ function listBlocks({
|
|
|
25675
25841
|
return listModerationState("block", { account, search, limit });
|
|
25676
25842
|
}
|
|
25677
25843
|
function syncBlocksEffect(accountId) {
|
|
25678
|
-
return
|
|
25844
|
+
return Effect53.gen(function* () {
|
|
25679
25845
|
const db = yield* trySync17(() => getNativeDb());
|
|
25680
25846
|
const resolvedAccountId = accountId || getDefaultAccountId(db);
|
|
25681
25847
|
const accountHandle = getAccountHandle(db, resolvedAccountId);
|
|
25682
25848
|
if (!accountHandle) {
|
|
25683
|
-
return yield*
|
|
25849
|
+
return yield* Effect53.fail(
|
|
25684
25850
|
new Error(`Unknown account: ${resolvedAccountId}`)
|
|
25685
25851
|
);
|
|
25686
25852
|
}
|
|
@@ -25701,10 +25867,8 @@ function syncBlocksEffect(accountId) {
|
|
|
25701
25867
|
}
|
|
25702
25868
|
};
|
|
25703
25869
|
}
|
|
25704
|
-
return yield*
|
|
25705
|
-
const me = yield*
|
|
25706
|
-
Effect54.mapError(toError22)
|
|
25707
|
-
);
|
|
25870
|
+
return yield* Effect53.gen(function* () {
|
|
25871
|
+
const me = yield* lookupAuthenticatedUserFreshEffect();
|
|
25708
25872
|
const sourceUserId = typeof me?.id === "string" && me.id.length > 0 ? me.id : null;
|
|
25709
25873
|
const sourceUsername = typeof me?.username === "string" ? me.username.replace(/^@/, "") : "";
|
|
25710
25874
|
const accountExternalUserId = account?.external_user_id?.trim() ?? "";
|
|
@@ -25748,9 +25912,7 @@ function syncBlocksEffect(accountId) {
|
|
|
25748
25912
|
let pageCount = 0;
|
|
25749
25913
|
let completed = false;
|
|
25750
25914
|
do {
|
|
25751
|
-
const page = yield*
|
|
25752
|
-
() => listBlockedUsers(sourceUserId, nextToken ?? void 0)
|
|
25753
|
-
).pipe(Effect54.mapError(toError22));
|
|
25915
|
+
const page = yield* listBlockedUsersEffect(sourceUserId, nextToken ?? void 0);
|
|
25754
25916
|
const pageProfileIds = yield* databaseWriteEffect((writeDb) => {
|
|
25755
25917
|
return page.items.map((user) => {
|
|
25756
25918
|
const resolved = upsertProfileFromXUser(writeDb, user);
|
|
@@ -25792,8 +25954,8 @@ function syncBlocksEffect(accountId) {
|
|
|
25792
25954
|
}
|
|
25793
25955
|
};
|
|
25794
25956
|
}).pipe(
|
|
25795
|
-
|
|
25796
|
-
(error) =>
|
|
25957
|
+
Effect53.catchAll(
|
|
25958
|
+
(error) => Effect53.succeed({
|
|
25797
25959
|
ok: false,
|
|
25798
25960
|
accountId: resolvedAccountId,
|
|
25799
25961
|
synced: remoteProfileIds.length > 0,
|
|
@@ -25944,7 +26106,7 @@ function registerModerationCommands({
|
|
|
25944
26106
|
|
|
25945
26107
|
// src/lib/blocklist.ts
|
|
25946
26108
|
import { readFile } from "node:fs/promises";
|
|
25947
|
-
import { Effect as
|
|
26109
|
+
import { Effect as Effect54 } from "effect";
|
|
25948
26110
|
function stripInlineCode(value) {
|
|
25949
26111
|
const match = value.match(/`([^`]+)`/);
|
|
25950
26112
|
return match?.[1] ?? value;
|
|
@@ -25981,15 +26143,15 @@ function parseBlocklistText(text) {
|
|
|
25981
26143
|
return items;
|
|
25982
26144
|
}
|
|
25983
26145
|
function importBlocklistEffect(accountId, filePath) {
|
|
25984
|
-
return
|
|
26146
|
+
return Effect54.gen(function* () {
|
|
25985
26147
|
const text = yield* tryPromise(() => readFile(filePath, "utf8"));
|
|
25986
26148
|
const queries = parseBlocklistText(text);
|
|
25987
26149
|
const items = [];
|
|
25988
26150
|
for (const query of queries) {
|
|
25989
26151
|
const blocked = yield* tryPromise(() => addBlock(accountId, query)).pipe(
|
|
25990
|
-
|
|
25991
|
-
|
|
25992
|
-
(error) =>
|
|
26152
|
+
Effect54.map((result2) => ({ ok: true, result: result2 })),
|
|
26153
|
+
Effect54.catchAll(
|
|
26154
|
+
(error) => Effect54.succeed({ ok: false, error })
|
|
25993
26155
|
)
|
|
25994
26156
|
);
|
|
25995
26157
|
if (!blocked.ok) {
|
|
@@ -26045,7 +26207,7 @@ function registerModerationCommands2(context) {
|
|
|
26045
26207
|
}
|
|
26046
26208
|
|
|
26047
26209
|
// src/lib/link-index.ts
|
|
26048
|
-
import { Effect as
|
|
26210
|
+
import { Effect as Effect55 } from "effect";
|
|
26049
26211
|
var DEFAULT_EXPAND_CONCURRENCY = 12;
|
|
26050
26212
|
function parseJsonField5(value, fallback) {
|
|
26051
26213
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -26207,7 +26369,7 @@ function expandWithConcurrencyEffect(db, urls, options) {
|
|
|
26207
26369
|
misses: 0,
|
|
26208
26370
|
errors: 0
|
|
26209
26371
|
};
|
|
26210
|
-
return
|
|
26372
|
+
return Effect55.forEach(
|
|
26211
26373
|
urls,
|
|
26212
26374
|
(url) => tryPromise(
|
|
26213
26375
|
() => expandUrls([url], {
|
|
@@ -26216,7 +26378,7 @@ function expandWithConcurrencyEffect(db, urls, options) {
|
|
|
26216
26378
|
timeoutMs: options.timeoutMs
|
|
26217
26379
|
})
|
|
26218
26380
|
).pipe(
|
|
26219
|
-
|
|
26381
|
+
Effect55.map((results) => {
|
|
26220
26382
|
const result = results[0];
|
|
26221
26383
|
if (result.source === "network") {
|
|
26222
26384
|
counts.networkExpansions++;
|
|
@@ -26233,10 +26395,10 @@ function expandWithConcurrencyEffect(db, urls, options) {
|
|
|
26233
26395
|
})
|
|
26234
26396
|
),
|
|
26235
26397
|
{ concurrency }
|
|
26236
|
-
).pipe(
|
|
26398
|
+
).pipe(Effect55.as(counts));
|
|
26237
26399
|
}
|
|
26238
26400
|
function backfillLinkIndexEffect(options = {}) {
|
|
26239
|
-
return
|
|
26401
|
+
return Effect55.gen(function* () {
|
|
26240
26402
|
const db = getNativeDb({ seedDemoData: false });
|
|
26241
26403
|
const { occurrences, entityExpansions } = rebuildOccurrences(
|
|
26242
26404
|
db,
|
|
@@ -26531,7 +26693,7 @@ import {
|
|
|
26531
26693
|
} from "node:fs";
|
|
26532
26694
|
import { appendFile, copyFile, rename, rm, writeFile } from "node:fs/promises";
|
|
26533
26695
|
import path11 from "node:path";
|
|
26534
|
-
import { Effect as
|
|
26696
|
+
import { Effect as Effect56 } from "effect";
|
|
26535
26697
|
var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
|
|
26536
26698
|
var PBS_PREFIXES = [
|
|
26537
26699
|
"/media/",
|
|
@@ -26550,13 +26712,13 @@ function toMediaError(error) {
|
|
|
26550
26712
|
return error instanceof Error ? error : new Error(String(error));
|
|
26551
26713
|
}
|
|
26552
26714
|
function tryMediaSync(try_) {
|
|
26553
|
-
return
|
|
26715
|
+
return Effect56.try({
|
|
26554
26716
|
try: try_,
|
|
26555
26717
|
catch: toMediaError
|
|
26556
26718
|
});
|
|
26557
26719
|
}
|
|
26558
26720
|
function tryMediaPromise(try_) {
|
|
26559
|
-
return
|
|
26721
|
+
return Effect56.tryPromise({
|
|
26560
26722
|
try: try_,
|
|
26561
26723
|
catch: toMediaError
|
|
26562
26724
|
});
|
|
@@ -26821,7 +26983,7 @@ function archivePathFor(item, mediaOriginalsDir) {
|
|
|
26821
26983
|
return archiveTweetDirs(mediaOriginalsDir, item.tweetId).map((tweetDir) => path11.join(tweetDir, fileName)).find((archivePath) => existsSync5(archivePath)) ?? null;
|
|
26822
26984
|
}
|
|
26823
26985
|
function reuseFromArchiveEffect(item, mediaOriginalsDir, maxBytes) {
|
|
26824
|
-
return
|
|
26986
|
+
return Effect56.gen(function* () {
|
|
26825
26987
|
const archivePath = archivePathFor(item, mediaOriginalsDir);
|
|
26826
26988
|
if (!archivePath || !existsSync5(archivePath)) return null;
|
|
26827
26989
|
const bytes = fileSize(archivePath);
|
|
@@ -26848,15 +27010,15 @@ function contentRangeTotal(response) {
|
|
|
26848
27010
|
return total ? Number(total) : null;
|
|
26849
27011
|
}
|
|
26850
27012
|
function writeResponseBodyEffect(response, tmpPath, append, maxBytes, initialBytes) {
|
|
26851
|
-
return
|
|
27013
|
+
return Effect56.gen(function* () {
|
|
26852
27014
|
if (!response.body)
|
|
26853
|
-
return yield*
|
|
27015
|
+
return yield* Effect56.fail(new Error("missing response body"));
|
|
26854
27016
|
let bytes = 0;
|
|
26855
27017
|
if (!append) {
|
|
26856
27018
|
yield* tryMediaPromise(() => writeFile(tmpPath, Buffer.alloc(0)));
|
|
26857
27019
|
}
|
|
26858
27020
|
const reader = response.body.getReader();
|
|
26859
|
-
const result = yield*
|
|
27021
|
+
const result = yield* Effect56.gen(function* () {
|
|
26860
27022
|
for (; ; ) {
|
|
26861
27023
|
const { done, value } = yield* tryMediaPromise(() => reader.read());
|
|
26862
27024
|
if (done) {
|
|
@@ -26866,31 +27028,31 @@ function writeResponseBodyEffect(response, tmpPath, append, maxBytes, initialByt
|
|
|
26866
27028
|
bytes += chunk.length;
|
|
26867
27029
|
if (initialBytes + bytes > maxBytes) {
|
|
26868
27030
|
yield* tryMediaPromise(() => rm(tmpPath, { force: true })).pipe(
|
|
26869
|
-
|
|
27031
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26870
27032
|
);
|
|
26871
|
-
return yield*
|
|
27033
|
+
return yield* Effect56.fail(new Error("max-bytes"));
|
|
26872
27034
|
}
|
|
26873
27035
|
yield* tryMediaPromise(() => appendFile(tmpPath, chunk));
|
|
26874
27036
|
}
|
|
26875
27037
|
return bytes;
|
|
26876
27038
|
}).pipe(
|
|
26877
|
-
|
|
26878
|
-
|
|
26879
|
-
(error) =>
|
|
27039
|
+
Effect56.map((writtenBytes) => ({ ok: true, writtenBytes })),
|
|
27040
|
+
Effect56.catchAll(
|
|
27041
|
+
(error) => Effect56.gen(function* () {
|
|
26880
27042
|
if (error.message === "max-bytes") {
|
|
26881
27043
|
yield* tryMediaPromise(() => rm(tmpPath, { force: true })).pipe(
|
|
26882
|
-
|
|
27044
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26883
27045
|
);
|
|
26884
27046
|
}
|
|
26885
27047
|
yield* tryMediaPromise(() => reader.cancel(error)).pipe(
|
|
26886
|
-
|
|
27048
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26887
27049
|
);
|
|
26888
27050
|
return { error, ok: false };
|
|
26889
27051
|
})
|
|
26890
27052
|
),
|
|
26891
|
-
|
|
27053
|
+
Effect56.ensuring(Effect56.sync(() => reader.releaseLock()))
|
|
26892
27054
|
);
|
|
26893
|
-
if (!result.ok) return yield*
|
|
27055
|
+
if (!result.ok) return yield* Effect56.fail(result.error);
|
|
26894
27056
|
return result.writtenBytes;
|
|
26895
27057
|
});
|
|
26896
27058
|
}
|
|
@@ -26902,13 +27064,13 @@ function fetchOneEffect({
|
|
|
26902
27064
|
userAgent,
|
|
26903
27065
|
maxBytes
|
|
26904
27066
|
}) {
|
|
26905
|
-
return
|
|
27067
|
+
return Effect56.gen(function* () {
|
|
26906
27068
|
let rateLimited = false;
|
|
26907
27069
|
for (let attempt = 0; attempt <= retryMax; attempt += 1) {
|
|
26908
27070
|
const partialBytes = item.kind === "image" ? 0 : fileSize(item.tmpPath);
|
|
26909
27071
|
if (partialBytes > maxBytes) {
|
|
26910
27072
|
yield* tryMediaPromise(() => rm(item.tmpPath, { force: true })).pipe(
|
|
26911
|
-
|
|
27073
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26912
27074
|
);
|
|
26913
27075
|
return fail(item, "max-bytes");
|
|
26914
27076
|
}
|
|
@@ -26920,9 +27082,9 @@ function fetchOneEffect({
|
|
|
26920
27082
|
}
|
|
26921
27083
|
})
|
|
26922
27084
|
).pipe(
|
|
26923
|
-
|
|
26924
|
-
|
|
26925
|
-
(error) =>
|
|
27085
|
+
Effect56.map((response2) => ({ ok: true, response: response2 })),
|
|
27086
|
+
Effect56.catchAll(
|
|
27087
|
+
(error) => Effect56.succeed({ error, ok: false })
|
|
26926
27088
|
)
|
|
26927
27089
|
);
|
|
26928
27090
|
if (!responseResult.ok) {
|
|
@@ -26933,7 +27095,7 @@ function fetchOneEffect({
|
|
|
26933
27095
|
rateLimited = true;
|
|
26934
27096
|
if (attempt < retryMax) {
|
|
26935
27097
|
yield* tryMediaPromise(() => sleep(1e3 * 2 ** attempt)).pipe(
|
|
26936
|
-
|
|
27098
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26937
27099
|
);
|
|
26938
27100
|
continue;
|
|
26939
27101
|
}
|
|
@@ -26945,7 +27107,7 @@ function fetchOneEffect({
|
|
|
26945
27107
|
const expectedTotal = contentRangeTotal(response) ?? (contentLength(response) ?? 0) + (response.status === 206 ? partialBytes : 0);
|
|
26946
27108
|
if (expectedTotal > maxBytes) {
|
|
26947
27109
|
yield* tryMediaPromise(() => rm(item.tmpPath, { force: true })).pipe(
|
|
26948
|
-
|
|
27110
|
+
Effect56.catchAll(() => Effect56.void)
|
|
26949
27111
|
);
|
|
26950
27112
|
return fail(item, "max-bytes");
|
|
26951
27113
|
}
|
|
@@ -26957,9 +27119,9 @@ function fetchOneEffect({
|
|
|
26957
27119
|
maxBytes,
|
|
26958
27120
|
append ? partialBytes : 0
|
|
26959
27121
|
).pipe(
|
|
26960
|
-
|
|
26961
|
-
|
|
26962
|
-
(error) =>
|
|
27122
|
+
Effect56.map((bytes) => ({ bytes, ok: true })),
|
|
27123
|
+
Effect56.catchAll(
|
|
27124
|
+
(error) => Effect56.succeed({ error, ok: false })
|
|
26963
27125
|
)
|
|
26964
27126
|
);
|
|
26965
27127
|
if (!bytesResult.ok) {
|
|
@@ -27033,7 +27195,7 @@ function runGroupEffect(items, parallel, pacingMs, now2, sleep, worker) {
|
|
|
27033
27195
|
);
|
|
27034
27196
|
})
|
|
27035
27197
|
);
|
|
27036
|
-
return
|
|
27198
|
+
return Effect56.forEach(pacedItems, runPaced, {
|
|
27037
27199
|
concurrency: Math.min(parallel, items.length),
|
|
27038
27200
|
discard: true
|
|
27039
27201
|
});
|
|
@@ -27042,7 +27204,7 @@ function fetchTweetMedia(options = {}) {
|
|
|
27042
27204
|
return runEffectPromise(fetchTweetMediaEffect(options));
|
|
27043
27205
|
}
|
|
27044
27206
|
function fetchTweetMediaEffect(options = {}) {
|
|
27045
|
-
return
|
|
27207
|
+
return Effect56.gen(function* () {
|
|
27046
27208
|
const now2 = options.now ?? Date.now;
|
|
27047
27209
|
const startedAt = now2();
|
|
27048
27210
|
const sleep = options.sleep ?? defaultSleep;
|
|
@@ -27109,7 +27271,7 @@ function fetchTweetMediaEffect(options = {}) {
|
|
|
27109
27271
|
retryMax,
|
|
27110
27272
|
userAgent,
|
|
27111
27273
|
maxBytes
|
|
27112
|
-
}).pipe(
|
|
27274
|
+
}).pipe(Effect56.map((fetched) => applyFetched(result, fetched)))
|
|
27113
27275
|
);
|
|
27114
27276
|
yield* runGroupEffect(
|
|
27115
27277
|
httpCandidates.filter((item) => item.kind === "image"),
|
|
@@ -27157,9 +27319,9 @@ function formatMediaFetchResult(result) {
|
|
|
27157
27319
|
}
|
|
27158
27320
|
|
|
27159
27321
|
// src/lib/whois.ts
|
|
27160
|
-
import { Effect as
|
|
27322
|
+
import { Effect as Effect57 } from "effect";
|
|
27161
27323
|
function trySync18(try_) {
|
|
27162
|
-
return
|
|
27324
|
+
return Effect57.try({
|
|
27163
27325
|
try: try_,
|
|
27164
27326
|
catch: (cause) => cause
|
|
27165
27327
|
});
|
|
@@ -27690,7 +27852,7 @@ function loadWhoisConversations(query, options, includeDms, context, limit) {
|
|
|
27690
27852
|
return [...merged.values()];
|
|
27691
27853
|
}
|
|
27692
27854
|
function runWhoisEffect(query, options = {}) {
|
|
27693
|
-
return
|
|
27855
|
+
return Effect57.gen(function* () {
|
|
27694
27856
|
const includeDms = options.dms ?? true;
|
|
27695
27857
|
const includeTweets = options.tweets ?? false;
|
|
27696
27858
|
const limit = options.limit ?? 10;
|
|
@@ -28141,7 +28303,7 @@ import { Readable as Readable2 } from "node:stream";
|
|
|
28141
28303
|
import { pathToFileURL } from "node:url";
|
|
28142
28304
|
|
|
28143
28305
|
// src/lib/http-effect.ts
|
|
28144
|
-
import { Effect as
|
|
28306
|
+
import { Effect as Effect58 } from "effect";
|
|
28145
28307
|
var LOCAL_WEB_PEER_HEADER = "x-birdclaw-local-peer";
|
|
28146
28308
|
|
|
28147
28309
|
// src/lib/production-server.ts
|
|
@@ -28415,7 +28577,7 @@ function registerStorageCommands({
|
|
|
28415
28577
|
}
|
|
28416
28578
|
|
|
28417
28579
|
// src/lib/authored-live.ts
|
|
28418
|
-
import { Effect as
|
|
28580
|
+
import { Effect as Effect59 } from "effect";
|
|
28419
28581
|
var AuthoredSyncError = class extends Error {
|
|
28420
28582
|
constructor(message, exitCode) {
|
|
28421
28583
|
super(message);
|
|
@@ -28657,10 +28819,10 @@ function resolveAuthoredIdentityEffect({
|
|
|
28657
28819
|
account,
|
|
28658
28820
|
db
|
|
28659
28821
|
}) {
|
|
28660
|
-
return
|
|
28822
|
+
return Effect59.gen(function* () {
|
|
28661
28823
|
const status = yield* liveTransportGateway.xurl.getTransportStatus();
|
|
28662
28824
|
if (status.availableTransport !== "xurl") {
|
|
28663
|
-
return yield*
|
|
28825
|
+
return yield* Effect59.fail(new AuthoredSyncError(status.statusText, 4));
|
|
28664
28826
|
}
|
|
28665
28827
|
const resolvedAccount = yield* trySync(
|
|
28666
28828
|
() => resolveLiveSyncAccount(db, account)
|
|
@@ -28675,7 +28837,7 @@ function resolveAuthoredIdentityEffect({
|
|
|
28675
28837
|
const authenticated = yield* liveTransportGateway.xurl.lookupAuthenticatedUser();
|
|
28676
28838
|
const authenticatedUser = userFromAuthenticatedPayload(authenticated);
|
|
28677
28839
|
if (!authenticatedUser?.id) {
|
|
28678
|
-
return yield*
|
|
28840
|
+
return yield* Effect59.fail(
|
|
28679
28841
|
new AuthoredSyncError(
|
|
28680
28842
|
"Could not resolve authenticated Twitter user id",
|
|
28681
28843
|
4
|
|
@@ -28683,7 +28845,7 @@ function resolveAuthoredIdentityEffect({
|
|
|
28683
28845
|
);
|
|
28684
28846
|
}
|
|
28685
28847
|
if (normalizeUsername(authenticatedUser.username) !== normalizeUsername(resolvedAccount.username)) {
|
|
28686
|
-
return yield*
|
|
28848
|
+
return yield* Effect59.fail(
|
|
28687
28849
|
new AuthoredSyncError(
|
|
28688
28850
|
`xurl is authenticated as @${authenticatedUser.username}, but selected account ${resolvedAccount.accountId} is @${resolvedAccount.username}. Link the account external_user_id or switch xurl login before syncing authored tweets.`,
|
|
28689
28851
|
4
|
|
@@ -28982,9 +29144,9 @@ function syncAuthoredTweetsEffect({
|
|
|
28982
29144
|
sinceId,
|
|
28983
29145
|
untilId
|
|
28984
29146
|
}) {
|
|
28985
|
-
return
|
|
29147
|
+
return Effect59.gen(function* () {
|
|
28986
29148
|
if (mode !== "xurl") {
|
|
28987
|
-
return yield*
|
|
29149
|
+
return yield* Effect59.fail(
|
|
28988
29150
|
new Error("authored sync only supports --mode xurl")
|
|
28989
29151
|
);
|
|
28990
29152
|
}
|
|
@@ -29047,7 +29209,7 @@ function syncAuthoredTweetsEffect({
|
|
|
29047
29209
|
sourceUser
|
|
29048
29210
|
})
|
|
29049
29211
|
).pipe(
|
|
29050
|
-
|
|
29212
|
+
Effect59.flatMap(
|
|
29051
29213
|
() => trySync(() => {
|
|
29052
29214
|
newestSeenId = maxTweetId(
|
|
29053
29215
|
newestSeenId,
|