birdclaw 0.9.2 → 0.9.4
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 +12 -0
- package/dist/cli/birdclaw.js +1296 -1082
- 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-DVQ3M870.js} +767 -710
- 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
|
@@ -22,7 +22,7 @@ import { Readable } from "node:stream";
|
|
|
22
22
|
import nodeHTTPS from "node:https";
|
|
23
23
|
import { Bell, Bookmark, CalendarDays, Check, ChevronDown, Database, Gauge, Globe2, Heart, Home, Inbox, Link as Link$1, Mail, MessagesSquare, Monitor, Moon, ShieldOff, Sun, UserSearch } from "lucide-react";
|
|
24
24
|
import { useQuery } from "@tanstack/react-query";
|
|
25
|
-
import { Data, Effect } from "effect";
|
|
25
|
+
import { Data, Duration, Effect, Schedule } from "effect";
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import { flushSync } from "react-dom";
|
|
28
28
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -521,7 +521,7 @@ function AppNav({ compact = false }) {
|
|
|
521
521
|
}
|
|
522
522
|
//#endregion
|
|
523
523
|
//#region src/styles.css?url
|
|
524
|
-
var styles_default = "/assets/styles-
|
|
524
|
+
var styles_default = "/assets/styles-DT4Pxoi-.css";
|
|
525
525
|
//#endregion
|
|
526
526
|
//#region src/routes/__root.tsx
|
|
527
527
|
var Route$24 = createRootRoute({
|
|
@@ -598,7 +598,7 @@ var $$splitComponentImporter = () => import("./routes-D9226vKz.js");
|
|
|
598
598
|
var Route$18 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter, "component") });
|
|
599
599
|
//#endregion
|
|
600
600
|
//#region src/lib/http-effect.ts
|
|
601
|
-
var LOCAL_HOSTS = new Set([
|
|
601
|
+
var LOCAL_HOSTS = /* @__PURE__ */ new Set([
|
|
602
602
|
"localhost",
|
|
603
603
|
"127.0.0.1",
|
|
604
604
|
"::1",
|
|
@@ -2565,7 +2565,7 @@ function yearFromTimestamp(value) {
|
|
|
2565
2565
|
function pathLeaf(relativePath) {
|
|
2566
2566
|
return relativePath.split("/").at(-1) ?? "";
|
|
2567
2567
|
}
|
|
2568
|
-
var JSON_URL_KEYS = new Set([
|
|
2568
|
+
var JSON_URL_KEYS = /* @__PURE__ */ new Set([
|
|
2569
2569
|
"url",
|
|
2570
2570
|
"expandedUrl",
|
|
2571
2571
|
"expanded_url",
|
|
@@ -3518,7 +3518,7 @@ function adaptLegacyTweetState(schemaVersion, tweets, collections, timelineEdges
|
|
|
3518
3518
|
const nextTimelineEdges = [...timelineEdges];
|
|
3519
3519
|
const collectionKeys = new Set(collections.map((row) => `${String(row.account_id)}\0${String(row.tweet_id)}\0${String(row.kind)}`));
|
|
3520
3520
|
const edgeKeys = new Set(timelineEdges.map((row) => `${String(row.account_id)}\0${String(row.tweet_id)}\0${String(row.kind)}`));
|
|
3521
|
-
const edgeKinds = new Set([
|
|
3521
|
+
const edgeKinds = /* @__PURE__ */ new Set([
|
|
3522
3522
|
"home",
|
|
3523
3523
|
"mention",
|
|
3524
3524
|
"authored",
|
|
@@ -5086,33 +5086,62 @@ function makeBirdStdoutTempEffect() {
|
|
|
5086
5086
|
force: true
|
|
5087
5087
|
})));
|
|
5088
5088
|
}
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5089
|
+
var runBirdJsonCommandEffect$1 = Effect.fn("bird.runJsonCommand")((args, timeoutMs) => Effect.scoped(Effect.gen(function* () {
|
|
5090
|
+
const birdCommand = yield* Effect.try({
|
|
5091
|
+
try: () => getBirdCommand(),
|
|
5092
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
5093
|
+
});
|
|
5094
|
+
const { stdoutPath } = yield* makeBirdStdoutTempEffect();
|
|
5095
|
+
yield* Effect.tryPromise({
|
|
5096
|
+
try: () => execFileAsync("/bin/bash", [
|
|
5097
|
+
"-c",
|
|
5098
|
+
BIRD_STDOUT_REDIRECT_SCRIPT,
|
|
5099
|
+
"birdclaw-bird",
|
|
5100
|
+
stdoutPath,
|
|
5101
|
+
birdCommand,
|
|
5102
|
+
...args
|
|
5103
|
+
], {
|
|
5104
|
+
maxBuffer: BIRD_JSON_MAX_BUFFER_BYTES,
|
|
5105
|
+
timeout: timeoutMs
|
|
5106
|
+
}),
|
|
5107
|
+
catch: (error) => formatBirdCommandError(error, birdCommand)
|
|
5108
|
+
});
|
|
5109
|
+
return yield* Effect.try({
|
|
5110
|
+
try: () => readFileSync(stdoutPath, "utf8"),
|
|
5111
|
+
catch: (error) => error
|
|
5112
|
+
});
|
|
5113
|
+
})));
|
|
5114
|
+
var runBirdJsonCommandAllowFailureEffect = Effect.fn("bird.runJsonCommandAllowFailure")((args, timeoutMs) => Effect.scoped(Effect.gen(function* () {
|
|
5115
|
+
const birdCommand = yield* Effect.try({
|
|
5116
|
+
try: () => getBirdCommand(),
|
|
5117
|
+
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
5118
|
+
});
|
|
5119
|
+
const { stdoutPath } = yield* makeBirdStdoutTempEffect();
|
|
5120
|
+
yield* Effect.tryPromise({
|
|
5121
|
+
try: () => execFileAsync("/bin/bash", [
|
|
5122
|
+
"-c",
|
|
5123
|
+
BIRD_STDOUT_REDIRECT_SCRIPT,
|
|
5124
|
+
"birdclaw-bird",
|
|
5125
|
+
stdoutPath,
|
|
5126
|
+
birdCommand,
|
|
5127
|
+
...args
|
|
5128
|
+
], {
|
|
5129
|
+
maxBuffer: BIRD_JSON_MAX_BUFFER_BYTES,
|
|
5130
|
+
timeout: timeoutMs
|
|
5131
|
+
}).catch((error) => {
|
|
5132
|
+
if (readFileSync(stdoutPath, "utf8").trim().length > 0) return {
|
|
5133
|
+
stdout: "",
|
|
5134
|
+
stderr: ""
|
|
5135
|
+
};
|
|
5136
|
+
throw formatBirdCommandError(error, birdCommand);
|
|
5137
|
+
}),
|
|
5138
|
+
catch: (error) => error
|
|
5139
|
+
});
|
|
5140
|
+
return yield* Effect.try({
|
|
5141
|
+
try: () => readFileSync(stdoutPath, "utf8"),
|
|
5142
|
+
catch: (error) => error
|
|
5143
|
+
});
|
|
5144
|
+
})));
|
|
5116
5145
|
function runBirdTweetJsonCommandEffect(args, timeoutMs) {
|
|
5117
5146
|
return runBirdJsonCommandEffect$1([...args, "--json-full"], timeoutMs).pipe(Effect.catchAll((error) => {
|
|
5118
5147
|
if (!isUnsupportedBirdOptionError(error, "--json-full")) return Effect.fail(error);
|
|
@@ -5124,6 +5153,13 @@ function getBirdTweetItems(payload, command) {
|
|
|
5124
5153
|
if (payload && typeof payload === "object" && Array.isArray(payload.tweets)) return payload.tweets;
|
|
5125
5154
|
throw new Error(`bird ${command} returned unexpected JSON`);
|
|
5126
5155
|
}
|
|
5156
|
+
function getBirdTweetItem(payload, command) {
|
|
5157
|
+
if (payload && typeof payload === "object") {
|
|
5158
|
+
const record = payload;
|
|
5159
|
+
if (typeof record.id === "string" && record.id.length > 0) return payload;
|
|
5160
|
+
}
|
|
5161
|
+
throw new Error(`bird ${command} returned unexpected JSON`);
|
|
5162
|
+
}
|
|
5127
5163
|
function toMediaEntities(media) {
|
|
5128
5164
|
if (!Array.isArray(media) || media.length === 0) return;
|
|
5129
5165
|
return { urls: media.filter((item) => typeof item?.url === "string" && item.url.length > 0).map((item, index) => ({
|
|
@@ -5259,29 +5295,31 @@ function normalizeBirdTweetsPayloadEffect(payload, command) {
|
|
|
5259
5295
|
catch: (error) => error
|
|
5260
5296
|
});
|
|
5261
5297
|
}
|
|
5262
|
-
function
|
|
5263
|
-
return Effect.
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
"-n",
|
|
5267
|
-
String(maxResults)
|
|
5268
|
-
])), "mentions");
|
|
5269
|
-
});
|
|
5270
|
-
}
|
|
5271
|
-
function listTweetsViaBirdCommandEffect({ command, maxResults, all, maxPages }) {
|
|
5272
|
-
return Effect.gen(function* () {
|
|
5273
|
-
const args = [
|
|
5274
|
-
command,
|
|
5275
|
-
"-n",
|
|
5276
|
-
String(maxResults)
|
|
5277
|
-
];
|
|
5278
|
-
if (all) {
|
|
5279
|
-
args.push("--all");
|
|
5280
|
-
if (maxPages !== void 0) args.push("--max-pages", String(maxPages));
|
|
5281
|
-
}
|
|
5282
|
-
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(args)), command);
|
|
5298
|
+
function normalizeBirdTweetItemEffect(payload, command) {
|
|
5299
|
+
return Effect.try({
|
|
5300
|
+
try: () => getBirdTweetItem(payload, command),
|
|
5301
|
+
catch: (error) => error
|
|
5283
5302
|
});
|
|
5284
5303
|
}
|
|
5304
|
+
var listMentionsViaBirdEffect = Effect.fn("bird.listMentions")(function* ({ maxResults }) {
|
|
5305
|
+
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect([
|
|
5306
|
+
"mentions",
|
|
5307
|
+
"-n",
|
|
5308
|
+
String(maxResults)
|
|
5309
|
+
])), "mentions");
|
|
5310
|
+
});
|
|
5311
|
+
var listTweetsViaBirdCommandEffect = Effect.fn("bird.listTweets")(function* ({ command, maxResults, all, maxPages }) {
|
|
5312
|
+
const args = [
|
|
5313
|
+
command,
|
|
5314
|
+
"-n",
|
|
5315
|
+
String(maxResults)
|
|
5316
|
+
];
|
|
5317
|
+
if (all) {
|
|
5318
|
+
args.push("--all");
|
|
5319
|
+
if (maxPages !== void 0) args.push("--max-pages", String(maxPages));
|
|
5320
|
+
}
|
|
5321
|
+
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(args)), command);
|
|
5322
|
+
});
|
|
5285
5323
|
function listLikedTweetsViaBirdEffect(options) {
|
|
5286
5324
|
return listTweetsViaBirdCommandEffect({
|
|
5287
5325
|
command: "likes",
|
|
@@ -5294,30 +5332,32 @@ function listBookmarkedTweetsViaBirdEffect(options) {
|
|
|
5294
5332
|
...options
|
|
5295
5333
|
});
|
|
5296
5334
|
}
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
}
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5335
|
+
var searchTweetsViaBirdEffect = Effect.fn("bird.searchTweets")(function* (query, options) {
|
|
5336
|
+
const args = [
|
|
5337
|
+
"search",
|
|
5338
|
+
query,
|
|
5339
|
+
"-n",
|
|
5340
|
+
String(options.maxResults)
|
|
5341
|
+
];
|
|
5342
|
+
if (options.all) args.push("--all");
|
|
5343
|
+
if (options.all && options.maxPages !== void 0) args.push("--max-pages", String(options.maxPages));
|
|
5344
|
+
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(args)), "search");
|
|
5345
|
+
});
|
|
5346
|
+
Effect.fn("bird.lookupTweetsByIds")(function* (ids) {
|
|
5347
|
+
if (ids.length === 0) return { data: [] };
|
|
5348
|
+
return normalizeBirdTweets(yield* Effect.forEach(ids, Effect.fn("bird.readTweet")(function* (id) {
|
|
5349
|
+
return yield* normalizeBirdTweetItemEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(["read", id])), "read");
|
|
5350
|
+
}), { concurrency: "unbounded" }));
|
|
5351
|
+
});
|
|
5352
|
+
var listHomeTimelineViaBirdEffect = Effect.fn("bird.listHomeTimeline")(function* ({ maxResults, following = true }) {
|
|
5353
|
+
const args = [
|
|
5354
|
+
"home",
|
|
5355
|
+
"-n",
|
|
5356
|
+
String(maxResults)
|
|
5357
|
+
];
|
|
5358
|
+
if (following) args.push("--following");
|
|
5359
|
+
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(args)), "home");
|
|
5360
|
+
});
|
|
5321
5361
|
function normalizeBirdFollowUsers(payload, command, maxResults) {
|
|
5322
5362
|
const rawPayload = payload;
|
|
5323
5363
|
const users = Array.isArray(rawPayload) ? rawPayload : rawPayload.users;
|
|
@@ -5341,28 +5381,81 @@ function normalizeBirdFollowUsersEffect(payload, command, maxResults) {
|
|
|
5341
5381
|
catch: (error) => error
|
|
5342
5382
|
});
|
|
5343
5383
|
}
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5384
|
+
var listFollowUsersViaBirdEffect = Effect.fn("bird.listFollowUsers")(function* ({ direction, userId, maxResults, all, maxPages }) {
|
|
5385
|
+
const args = [
|
|
5386
|
+
direction,
|
|
5387
|
+
"-n",
|
|
5388
|
+
String(maxResults),
|
|
5389
|
+
"--json"
|
|
5390
|
+
];
|
|
5391
|
+
if (userId) args.push("--user", userId);
|
|
5392
|
+
if (all) args.push("--all");
|
|
5393
|
+
if (maxPages !== void 0) args.push("--max-pages", String(maxPages));
|
|
5394
|
+
return yield* normalizeBirdFollowUsersEffect(yield* parseBirdJsonEffect(yield* runBirdJsonCommandEffect$1(args)), direction, maxResults);
|
|
5395
|
+
});
|
|
5396
|
+
function normalizeBirdLists(payload) {
|
|
5397
|
+
if (!Array.isArray(payload)) throw new Error("bird lists returned unexpected JSON");
|
|
5398
|
+
const data = payload.map((value) => {
|
|
5399
|
+
const item = value;
|
|
5400
|
+
if (!item.id || !item.name) return null;
|
|
5401
|
+
return {
|
|
5402
|
+
id: item.id,
|
|
5403
|
+
name: item.name,
|
|
5404
|
+
...item.description ? { description: item.description } : {},
|
|
5405
|
+
...typeof item.memberCount === "number" ? { memberCount: item.memberCount } : {},
|
|
5406
|
+
...typeof item.subscriberCount === "number" ? { followerCount: item.subscriberCount } : {},
|
|
5407
|
+
...typeof item.isPrivate === "boolean" ? { isPrivate: item.isPrivate } : {},
|
|
5408
|
+
...item.owner?.id ? { ownerId: item.owner.id } : {},
|
|
5409
|
+
...item.owner?.username ? { ownerUsername: item.owner.username } : {},
|
|
5410
|
+
...item.owner?.name ? { ownerName: item.owner.name } : {},
|
|
5411
|
+
...item.createdAt ? { createdAt: item.createdAt } : {},
|
|
5412
|
+
raw: value
|
|
5413
|
+
};
|
|
5414
|
+
}).filter((item) => Boolean(item));
|
|
5415
|
+
return {
|
|
5416
|
+
data,
|
|
5417
|
+
meta: {
|
|
5418
|
+
result_count: data.length,
|
|
5419
|
+
next_token: null
|
|
5420
|
+
}
|
|
5421
|
+
};
|
|
5357
5422
|
}
|
|
5358
|
-
function
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5423
|
+
Effect.fn("bird.listOwnedXLists")(function* ({ maxResults }) {
|
|
5424
|
+
const payload = yield* parseBirdJsonEffect(yield* runBirdJsonCommandEffect$1([
|
|
5425
|
+
"lists",
|
|
5426
|
+
"-n",
|
|
5427
|
+
String(maxResults),
|
|
5428
|
+
"--json"
|
|
5429
|
+
]));
|
|
5430
|
+
return yield* Effect.try({
|
|
5431
|
+
try: () => normalizeBirdLists(payload),
|
|
5432
|
+
catch: (error) => error
|
|
5364
5433
|
});
|
|
5365
|
-
}
|
|
5434
|
+
});
|
|
5435
|
+
Effect.fn("bird.listXListMembers")(function* ({ listId, maxResults, maxPages = 1 }) {
|
|
5436
|
+
const args = [
|
|
5437
|
+
"list-members",
|
|
5438
|
+
listId,
|
|
5439
|
+
"-n",
|
|
5440
|
+
String(maxResults),
|
|
5441
|
+
"--json"
|
|
5442
|
+
];
|
|
5443
|
+
if (maxPages > 1) args.push("--all", "--max-pages", String(maxPages));
|
|
5444
|
+
const payload = yield* parseBirdJsonEffect(yield* runBirdJsonCommandEffect$1(args));
|
|
5445
|
+
const normalized = yield* normalizeBirdFollowUsersEffect(payload, "list-members", maxResults);
|
|
5446
|
+
if (Array.isArray(payload)) normalized.meta = {
|
|
5447
|
+
...normalized.meta,
|
|
5448
|
+
pagination_known_complete: false,
|
|
5449
|
+
pagination_inferred_complete: normalized.data.length < maxResults
|
|
5450
|
+
};
|
|
5451
|
+
return normalized;
|
|
5452
|
+
});
|
|
5453
|
+
var listThreadViaBirdEffect = Effect.fn("bird.listThread")(function* ({ tweetId, all, maxPages, timeoutMs }) {
|
|
5454
|
+
const args = ["thread", tweetId];
|
|
5455
|
+
if (all) args.push("--all");
|
|
5456
|
+
if (maxPages !== void 0) args.push("--max-pages", String(maxPages));
|
|
5457
|
+
return yield* normalizeBirdTweetsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdTweetJsonCommandEffect(args, timeoutMs)), "thread");
|
|
5458
|
+
});
|
|
5366
5459
|
function normalizeBirdDmsPayloadEffect(payload) {
|
|
5367
5460
|
return Effect.try({
|
|
5368
5461
|
try: () => {
|
|
@@ -5388,51 +5481,60 @@ function parseBirdWhoami(stdout) {
|
|
|
5388
5481
|
...name ? { name } : {}
|
|
5389
5482
|
};
|
|
5390
5483
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
catch: (error) => error
|
|
5397
|
-
});
|
|
5484
|
+
var getAuthenticatedBirdAccountEffect = Effect.fn("bird.getAuthenticatedAccount")(function* () {
|
|
5485
|
+
const stdout = yield* runBirdJsonCommandEffect$1(["whoami"]);
|
|
5486
|
+
return yield* Effect.try({
|
|
5487
|
+
try: () => parseBirdWhoami(stdout),
|
|
5488
|
+
catch: (error) => error
|
|
5398
5489
|
});
|
|
5399
|
-
}
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5490
|
+
});
|
|
5491
|
+
var listDirectMessagesViaBirdEffect = Effect.fn("bird.listDirectMessages")(function* ({ maxResults, inbox = "all", maxPages, allPages = false, pageDelayMs }) {
|
|
5492
|
+
const args = [
|
|
5493
|
+
"dms",
|
|
5494
|
+
"-n",
|
|
5495
|
+
String(maxResults),
|
|
5496
|
+
"--json"
|
|
5497
|
+
];
|
|
5498
|
+
if (inbox !== "all") args.push("--inbox", inbox);
|
|
5499
|
+
if (allPages) args.push("--all-pages");
|
|
5500
|
+
else if (typeof maxPages === "number") args.push("--max-pages", String(maxPages));
|
|
5501
|
+
if (typeof pageDelayMs === "number" && pageDelayMs > 0) args.push("--page-delay-ms", String(pageDelayMs));
|
|
5502
|
+
return yield* normalizeBirdDmsPayloadEffect(yield* parseBirdJsonEffect(yield* runBirdJsonCommandEffect$1(args)));
|
|
5503
|
+
});
|
|
5504
|
+
Effect.fn("bird.runDirectMessageRequestMutation")(function* ({ action, conversationId, maxPages, allPages = false }) {
|
|
5505
|
+
const command = action === "accept" ? "dm-accept" : action === "reject" ? "dm-reject" : "dm-block";
|
|
5506
|
+
const args = [
|
|
5507
|
+
command,
|
|
5508
|
+
conversationId,
|
|
5509
|
+
"--json"
|
|
5510
|
+
];
|
|
5511
|
+
if (action === "block") {
|
|
5409
5512
|
if (allPages) args.push("--all-pages");
|
|
5410
5513
|
else if (typeof maxPages === "number") args.push("--max-pages", String(maxPages));
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
}
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5514
|
+
}
|
|
5515
|
+
const payload = yield* parseBirdJsonEffect(yield* runBirdJsonCommandAllowFailureEffect(args));
|
|
5516
|
+
if (payload && typeof payload === "object" && typeof payload.success === "boolean") return payload;
|
|
5517
|
+
throw new Error(`bird ${command} returned unexpected JSON`);
|
|
5518
|
+
});
|
|
5519
|
+
var lookupProfileViaBirdEffect$1 = Effect.fn("bird.lookupProfile")(function* (usernameOrId) {
|
|
5520
|
+
const target = usernameOrId.trim().replace(/^@/, "");
|
|
5521
|
+
if (!target) return null;
|
|
5522
|
+
return toXurlMentionUser((yield* parseBirdJsonEffect(yield* runBirdJsonCommandEffect$1([
|
|
5523
|
+
"user",
|
|
5524
|
+
target,
|
|
5525
|
+
"--json",
|
|
5526
|
+
"--profile-only"
|
|
5527
|
+
]).pipe(Effect.catchAll((error) => {
|
|
5528
|
+
if (!isUnsupportedBirdOptionError(error, "--profile-only")) return Effect.fail(error);
|
|
5529
|
+
return runBirdJsonCommandEffect$1([
|
|
5420
5530
|
"user",
|
|
5421
5531
|
target,
|
|
5422
5532
|
"--json",
|
|
5423
|
-
"--
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
target,
|
|
5429
|
-
"--json",
|
|
5430
|
-
"--count",
|
|
5431
|
-
"1"
|
|
5432
|
-
]);
|
|
5433
|
-
})))).user);
|
|
5434
|
-
});
|
|
5435
|
-
}
|
|
5533
|
+
"--count",
|
|
5534
|
+
"1"
|
|
5535
|
+
]);
|
|
5536
|
+
})))).user);
|
|
5537
|
+
});
|
|
5436
5538
|
function toXurlMentionUser(user) {
|
|
5437
5539
|
if (!user?.id || !user.username) return null;
|
|
5438
5540
|
return {
|
|
@@ -5454,7 +5556,7 @@ function toXurlMentionUser(user) {
|
|
|
5454
5556
|
}
|
|
5455
5557
|
};
|
|
5456
5558
|
}
|
|
5457
|
-
|
|
5559
|
+
var lookupProfilesViaBirdEffect = Effect.fn("bird.lookupProfiles")((usernameOrIds) => {
|
|
5458
5560
|
const targets = Array.from(new Set(usernameOrIds.map((target) => target.trim().replace(/^@/, "")).filter((target) => target.length > 0)));
|
|
5459
5561
|
if (targets.length === 0) return Effect.succeed([]);
|
|
5460
5562
|
return runBirdJsonCommandEffect$1([
|
|
@@ -5486,7 +5588,7 @@ function lookupProfilesViaBirdEffect(usernameOrIds) {
|
|
|
5486
5588
|
error: lookupError instanceof Error ? lookupError.message : String(lookupError)
|
|
5487
5589
|
}))), { concurrency: "unbounded" });
|
|
5488
5590
|
}));
|
|
5489
|
-
}
|
|
5591
|
+
});
|
|
5490
5592
|
//#endregion
|
|
5491
5593
|
//#region src/lib/xurl.ts
|
|
5492
5594
|
var TRANSPORT_STATUS_TTL_MS = 5 * 6e4;
|
|
@@ -5499,8 +5601,8 @@ var RICH_USER_FIELDS = "description,entities,location,public_metrics,profile_ima
|
|
|
5499
5601
|
var DM_EVENT_FIELDS = "attachments,created_at,dm_conversation_id,entities,event_type,id,participant_ids,referenced_tweets,sender_id,text";
|
|
5500
5602
|
var THREAD_TWEET_FIELDS = "created_at,conversation_id,entities,public_metrics,referenced_tweets,in_reply_to_user_id,attachments";
|
|
5501
5603
|
var BOOKMARKS_MAX_RESULTS_CAP = 90;
|
|
5502
|
-
var
|
|
5503
|
-
var
|
|
5604
|
+
var cachedTransportStatus = Effect.runSync(Effect.cachedWithTTL(Effect.suspend(() => readTransportStatusEffect()), Duration.millis(TRANSPORT_STATUS_TTL_MS)));
|
|
5605
|
+
var [cachedAuthenticatedUser, invalidateAuthenticatedUser] = Effect.runSync(Effect.cachedInvalidateWithTTL(Effect.suspend(() => lookupAuthenticatedUserFreshEffect()), Duration.millis(AUTHENTICATED_USER_TTL_MS)));
|
|
5504
5606
|
var oauth2CandidateCache = /* @__PURE__ */ new Map();
|
|
5505
5607
|
function liveWritesDisabled$3() {
|
|
5506
5608
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
@@ -5522,8 +5624,15 @@ function formatExecError$1(error, fallback) {
|
|
|
5522
5624
|
if ("stderr" in error && typeof error.stderr === "string" && error.stderr.trim().length > 0) parts.push(stripAnsi$1(error.stderr).trim());
|
|
5523
5625
|
return parts.join("\n");
|
|
5524
5626
|
}
|
|
5525
|
-
|
|
5526
|
-
|
|
5627
|
+
var XurlCommandError = class extends Data.TaggedError("XurlCommandError") {};
|
|
5628
|
+
function toXurlCommandError(cause, args) {
|
|
5629
|
+
if (cause instanceof XurlCommandError) return cause;
|
|
5630
|
+
return new XurlCommandError({
|
|
5631
|
+
message: formatExecError$1(cause, `xurl ${args.join(" ")} failed`),
|
|
5632
|
+
commandArgs: args,
|
|
5633
|
+
rateLimited: isRateLimitedFailure(cause),
|
|
5634
|
+
cause
|
|
5635
|
+
});
|
|
5527
5636
|
}
|
|
5528
5637
|
function normalizeError(error) {
|
|
5529
5638
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -5539,11 +5648,11 @@ function parseErrorPayload(error) {
|
|
|
5539
5648
|
return null;
|
|
5540
5649
|
}
|
|
5541
5650
|
}
|
|
5542
|
-
function
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
return Math.min(
|
|
5651
|
+
function isRateLimitedFailure(error) {
|
|
5652
|
+
return Number(parseErrorPayload(error)?.status ?? 0) === 429;
|
|
5653
|
+
}
|
|
5654
|
+
function jsonRetryDelayMs(retries) {
|
|
5655
|
+
return Math.min(getJsonRetryBaseDelayMs() * 2 ** retries, 3e4);
|
|
5547
5656
|
}
|
|
5548
5657
|
function emitJsonCommandAttempt(options, attempt) {
|
|
5549
5658
|
try {
|
|
@@ -5562,93 +5671,60 @@ function hasXurlEffect() {
|
|
|
5562
5671
|
function isUnauthenticatedXurlStatus(status) {
|
|
5563
5672
|
return /no apps registered|no authenticated user|not authenticated|not logged in/i.test(status);
|
|
5564
5673
|
}
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
if (isUnauthenticatedXurlStatus(rawStatus)) return {
|
|
5578
|
-
installed: true,
|
|
5579
|
-
availableTransport: "local",
|
|
5580
|
-
statusText: "xurl installed but not authenticated. local (bird) mode active.",
|
|
5581
|
-
rawStatus
|
|
5582
|
-
};
|
|
5583
|
-
return {
|
|
5584
|
-
installed: true,
|
|
5585
|
-
availableTransport: "xurl",
|
|
5586
|
-
statusText: "xurl available",
|
|
5587
|
-
rawStatus
|
|
5588
|
-
};
|
|
5589
|
-
}), Effect.catchAll((error) => Effect.succeed({
|
|
5674
|
+
var readTransportStatusEffect = Effect.fn("xurl.readTransportStatus")(function* () {
|
|
5675
|
+
if (!(yield* hasXurlEffect())) return {
|
|
5676
|
+
installed: false,
|
|
5677
|
+
availableTransport: "local",
|
|
5678
|
+
statusText: "xurl not installed. local mode active."
|
|
5679
|
+
};
|
|
5680
|
+
return yield* runSubprocessEffect({
|
|
5681
|
+
command: "xurl",
|
|
5682
|
+
args: ["auth", "status"]
|
|
5683
|
+
}).pipe(Effect.map(({ stdout }) => {
|
|
5684
|
+
const rawStatus = stdout.trim();
|
|
5685
|
+
if (isUnauthenticatedXurlStatus(rawStatus)) return {
|
|
5590
5686
|
installed: true,
|
|
5591
5687
|
availableTransport: "local",
|
|
5592
|
-
statusText:
|
|
5593
|
-
|
|
5594
|
-
});
|
|
5595
|
-
}
|
|
5596
|
-
function getTransportStatusEffect() {
|
|
5597
|
-
return Effect.gen(function* () {
|
|
5598
|
-
const now = Date.now();
|
|
5599
|
-
if (transportStatusCache?.value && transportStatusCache.expiresAt > now) return transportStatusCache.value;
|
|
5600
|
-
if (transportStatusCache?.pending) {
|
|
5601
|
-
const status = yield* Effect.tryPromise({
|
|
5602
|
-
try: () => transportStatusCache?.pending ?? Promise.resolve(void 0),
|
|
5603
|
-
catch: normalizeError
|
|
5604
|
-
});
|
|
5605
|
-
if (status) return status;
|
|
5606
|
-
}
|
|
5607
|
-
const pending = runEffectPromise(readTransportStatusEffect());
|
|
5608
|
-
transportStatusCache = {
|
|
5609
|
-
expiresAt: 0,
|
|
5610
|
-
pending
|
|
5688
|
+
statusText: "xurl installed but not authenticated. local (bird) mode active.",
|
|
5689
|
+
rawStatus
|
|
5611
5690
|
};
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
}).pipe(Effect.flatMap(() => Effect.fail(error)))));
|
|
5618
|
-
transportStatusCache = {
|
|
5619
|
-
expiresAt: Date.now() + TRANSPORT_STATUS_TTL_MS,
|
|
5620
|
-
value: status
|
|
5691
|
+
return {
|
|
5692
|
+
installed: true,
|
|
5693
|
+
availableTransport: "xurl",
|
|
5694
|
+
statusText: "xurl available",
|
|
5695
|
+
rawStatus
|
|
5621
5696
|
};
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5697
|
+
}), Effect.catchAll((error) => Effect.succeed({
|
|
5698
|
+
installed: true,
|
|
5699
|
+
availableTransport: "local",
|
|
5700
|
+
statusText: `xurl detected but auth unavailable: ${error instanceof Error && !(error instanceof SubprocessError && !error.causeWasError) ? error.message : "unknown error"}`
|
|
5701
|
+
})));
|
|
5702
|
+
});
|
|
5703
|
+
function getTransportStatusEffect() {
|
|
5704
|
+
return cachedTransportStatus;
|
|
5627
5705
|
}
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
if (
|
|
5631
|
-
if (e2eFakeLiveWritesEnabled$2()) return {
|
|
5632
|
-
ok: true,
|
|
5633
|
-
output: "e2e fake live write"
|
|
5634
|
-
};
|
|
5635
|
-
return {
|
|
5636
|
-
ok: false,
|
|
5637
|
-
output: "live writes disabled"
|
|
5638
|
-
};
|
|
5639
|
-
}
|
|
5640
|
-
return yield* runSubprocessEffect({
|
|
5641
|
-
command: "xurl",
|
|
5642
|
-
args
|
|
5643
|
-
}).pipe(Effect.map(({ stdout, stderr }) => ({
|
|
5706
|
+
var runShortcutEffect = Effect.fn("xurl.runShortcut")(function* (args) {
|
|
5707
|
+
if (liveWritesDisabled$3()) {
|
|
5708
|
+
if (e2eFakeLiveWritesEnabled$2()) return {
|
|
5644
5709
|
ok: true,
|
|
5645
|
-
output:
|
|
5646
|
-
}
|
|
5710
|
+
output: "e2e fake live write"
|
|
5711
|
+
};
|
|
5712
|
+
return {
|
|
5647
5713
|
ok: false,
|
|
5648
|
-
output:
|
|
5649
|
-
}
|
|
5650
|
-
}
|
|
5651
|
-
|
|
5714
|
+
output: "live writes disabled"
|
|
5715
|
+
};
|
|
5716
|
+
}
|
|
5717
|
+
return yield* runSubprocessEffect({
|
|
5718
|
+
command: "xurl",
|
|
5719
|
+
args
|
|
5720
|
+
}).pipe(Effect.map(({ stdout, stderr }) => ({
|
|
5721
|
+
ok: true,
|
|
5722
|
+
output: stdout || stderr
|
|
5723
|
+
})), Effect.catchAll((error) => Effect.succeed({
|
|
5724
|
+
ok: false,
|
|
5725
|
+
output: formatExecError$1(error, "xurl execution failed")
|
|
5726
|
+
})));
|
|
5727
|
+
});
|
|
5652
5728
|
function execXurlJsonEffect(args, timeoutMs, signal) {
|
|
5653
5729
|
return runSubprocessEffect({
|
|
5654
5730
|
command: "xurl",
|
|
@@ -5679,35 +5755,31 @@ function execXurlTextEffect(args, deadlineMs) {
|
|
|
5679
5755
|
function parseJsonPayloadEffect(stdout, args) {
|
|
5680
5756
|
return Effect.try({
|
|
5681
5757
|
try: () => JSON.parse(stdout),
|
|
5682
|
-
catch: (error) =>
|
|
5758
|
+
catch: (error) => toXurlCommandError(error, args)
|
|
5683
5759
|
});
|
|
5684
5760
|
}
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5761
|
+
var runJsonCommandEffect = Effect.fn("xurl.runJsonCommand")(function* (args, options = {}) {
|
|
5762
|
+
const deadlineMs = options.deadlineMs ?? (typeof options.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
|
|
5763
|
+
let attempt = 0;
|
|
5764
|
+
const attemptCommand = Effect.suspend(() => execXurlJsonEffect(args, deadlineMs === void 0 ? void 0 : Math.max(0, deadlineMs - Date.now()), options.signal)).pipe(Effect.flatMap(({ stdout }) => parseJsonPayloadEffect(stdout, args)), Effect.mapError((error) => toXurlCommandError(error, args)), Effect.tap(() => Effect.sync(() => emitJsonCommandAttempt(options, {
|
|
5765
|
+
args,
|
|
5766
|
+
attempt,
|
|
5767
|
+
status: "ok"
|
|
5768
|
+
}))), Effect.tapError((error) => Effect.sync(() => {
|
|
5769
|
+
emitJsonCommandAttempt(options, {
|
|
5689
5770
|
args,
|
|
5690
5771
|
attempt,
|
|
5691
|
-
status: "
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
const remainingMs = deadlineMs ? Math.max(0, deadlineMs - Date.now()) : void 0;
|
|
5703
|
-
if (remainingMs !== void 0 && retryDelayMs >= remainingMs) return Effect.fail(formatXurlCommandError(error, args));
|
|
5704
|
-
return Effect.sleep(retryDelayMs).pipe(Effect.flatMap(() => runJsonCommandEffect(args, {
|
|
5705
|
-
...options,
|
|
5706
|
-
deadlineMs
|
|
5707
|
-
}, attempt + 1)));
|
|
5708
|
-
}));
|
|
5709
|
-
});
|
|
5710
|
-
}
|
|
5772
|
+
status: error.rateLimited ? "rate_limited" : "error",
|
|
5773
|
+
error
|
|
5774
|
+
});
|
|
5775
|
+
attempt += 1;
|
|
5776
|
+
})));
|
|
5777
|
+
const rateLimitRetries = Schedule.recurs(JSON_RETRY_LIMIT - 1).pipe(Schedule.whileInput((error) => error.rateLimited && options.signal?.aborted !== true), Schedule.check((_error, retries) => {
|
|
5778
|
+
if (deadlineMs === void 0) return true;
|
|
5779
|
+
return jsonRetryDelayMs(retries) < Math.max(0, deadlineMs - Date.now());
|
|
5780
|
+
}), Schedule.addDelay((retries) => Duration.millis(jsonRetryDelayMs(retries))));
|
|
5781
|
+
return yield* Effect.retry(attemptCommand, rateLimitRetries);
|
|
5782
|
+
});
|
|
5711
5783
|
function cleanXurlUsernameLabel(username) {
|
|
5712
5784
|
const label = username?.trim().replace(/^@/, "");
|
|
5713
5785
|
return label && label !== "-" && label !== "–" && label !== "(none)" && label.toLowerCase() !== "none" && label.toLowerCase() !== "unknown" && /^[^\s]{1,128}$/.test(label) ? label : void 0;
|
|
@@ -5744,29 +5816,25 @@ function parseOAuth2UsernamesFromStatus(rawStatus) {
|
|
|
5744
5816
|
}
|
|
5745
5817
|
return usernames;
|
|
5746
5818
|
}
|
|
5747
|
-
|
|
5748
|
-
return execXurlTextEffect(["auth", "status"], deadlineMs).pipe(Effect.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)), Effect.catchAll(() => Effect.succeed([])));
|
|
5749
|
-
}
|
|
5819
|
+
var readOAuth2UsernameCandidatesEffect = Effect.fn("xurl.readOAuth2UsernameCandidates")((deadlineMs) => execXurlTextEffect(["auth", "status"], deadlineMs).pipe(Effect.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)), Effect.catchAll(() => Effect.succeed([]))));
|
|
5750
5820
|
function readXurlOAuth2AccountsEffect() {
|
|
5751
5821
|
return readOAuth2UsernameCandidatesEffect().pipe(Effect.map((candidates) => candidates.map((candidate) => ({
|
|
5752
5822
|
...candidate.app ? { app: candidate.app } : {},
|
|
5753
5823
|
...candidate.username ? { username: candidate.username } : {}
|
|
5754
5824
|
}))));
|
|
5755
5825
|
}
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
});
|
|
5769
|
-
}
|
|
5826
|
+
var lookupOAuth2UsernameForAccountEffect = Effect.fn("xurl.lookupOAuth2UsernameForAccount")(function* (expectedUsername, attemptedUsernames, deadlineMs, knownCandidates) {
|
|
5827
|
+
const expected = comparableXurlUsername(expectedUsername);
|
|
5828
|
+
if (!expected) return void 0;
|
|
5829
|
+
const candidates = knownCandidates ?? (yield* readOAuth2UsernameCandidatesEffect(deadlineMs));
|
|
5830
|
+
for (const candidate of candidates) {
|
|
5831
|
+
const candidateKey = `${candidate.app ?? "default"}:${candidate.username}`;
|
|
5832
|
+
if (attemptedUsernames.has(candidateKey)) continue;
|
|
5833
|
+
const payload = yield* runJsonCommandEffect(oauth2ArgsForCandidate(candidate, ["/2/users/me"]), { deadlineMs }).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
5834
|
+
const user = payload ? authenticatedUserFromPayload(payload) : null;
|
|
5835
|
+
if (comparableXurlUsername(String(user?.username ?? "")) === expected) return candidate;
|
|
5836
|
+
}
|
|
5837
|
+
});
|
|
5770
5838
|
function oauth2ArgsForCandidate(candidate, args) {
|
|
5771
5839
|
return [
|
|
5772
5840
|
...candidate?.app ? ["--app", candidate.app] : [],
|
|
@@ -5787,88 +5855,136 @@ function configuredOAuth2Candidate(primaryUsername) {
|
|
|
5787
5855
|
...effectiveUsername ? { username: effectiveUsername } : {}
|
|
5788
5856
|
};
|
|
5789
5857
|
}
|
|
5790
|
-
|
|
5858
|
+
var runOAuth2JsonCommandEffect = Effect.fn("xurl.runOAuth2JsonCommand")(function* ({ args, username, options, useConfiguredCandidate = true }) {
|
|
5791
5859
|
const primaryUsername = cleanXurlUsernameLabel(username);
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5860
|
+
const deadlineMs = options?.deadlineMs ?? (typeof options?.timeoutMs === "number" && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? Date.now() + options.timeoutMs : void 0);
|
|
5861
|
+
const scopedOptions = {
|
|
5862
|
+
...options,
|
|
5863
|
+
deadlineMs
|
|
5864
|
+
};
|
|
5865
|
+
let authCandidate = primaryUsername ? { username: primaryUsername } : void 0;
|
|
5866
|
+
const configuredCandidate = useConfiguredCandidate ? configuredOAuth2Candidate(primaryUsername) : void 0;
|
|
5867
|
+
if (configuredCandidate) authCandidate = configuredCandidate;
|
|
5868
|
+
else if (primaryUsername) {
|
|
5869
|
+
const cacheKey = primaryUsername.toLowerCase();
|
|
5870
|
+
const cachedCandidate = oauth2CandidateCache.get(cacheKey);
|
|
5871
|
+
if (cachedCandidate && cachedCandidate.expiresAt > Date.now()) authCandidate = cachedCandidate.value;
|
|
5872
|
+
else {
|
|
5873
|
+
const candidates = yield* readOAuth2UsernameCandidatesEffect(deadlineMs);
|
|
5874
|
+
const primaryCandidates = candidates.filter((candidate) => candidate.username === primaryUsername);
|
|
5875
|
+
if (primaryCandidates.length === 1) authCandidate = primaryCandidates[0];
|
|
5876
|
+
else if (primaryCandidates.length > 1) authCandidate = (yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates)) ?? primaryCandidates[0] ?? { username: primaryUsername };
|
|
5805
5877
|
else {
|
|
5806
|
-
const
|
|
5807
|
-
|
|
5808
|
-
if (primaryCandidates.length === 1) authCandidate = primaryCandidates[0];
|
|
5809
|
-
else if (primaryCandidates.length > 1) authCandidate = (yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates)) ?? primaryCandidates[0] ?? { username: primaryUsername };
|
|
5810
|
-
else {
|
|
5811
|
-
const fallbackUsername = yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates);
|
|
5812
|
-
if (fallbackUsername) authCandidate = fallbackUsername;
|
|
5813
|
-
}
|
|
5814
|
-
if (authCandidate) oauth2CandidateCache.set(cacheKey, {
|
|
5815
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5816
|
-
value: authCandidate
|
|
5817
|
-
});
|
|
5878
|
+
const fallbackUsername = yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates);
|
|
5879
|
+
if (fallbackUsername) authCandidate = fallbackUsername;
|
|
5818
5880
|
}
|
|
5881
|
+
if (authCandidate) oauth2CandidateCache.set(cacheKey, {
|
|
5882
|
+
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5883
|
+
value: authCandidate
|
|
5884
|
+
});
|
|
5819
5885
|
}
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
return lookupOAuth2UsernameForAccountEffect(primaryUsername, attempted, deadlineMs).pipe(Effect.flatMap((fallbackUsername) => {
|
|
5827
|
-
if (!fallbackUsername) return Effect.fail(error);
|
|
5828
|
-
oauth2CandidateCache.set(primaryUsername.toLowerCase(), {
|
|
5829
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5830
|
-
value: fallbackUsername
|
|
5831
|
-
});
|
|
5832
|
-
return runJsonCommandEffect(oauth2ArgsForCandidate(fallbackUsername, args), scopedOptions);
|
|
5833
|
-
}), Effect.catchAll(() => Effect.fail(error)));
|
|
5834
|
-
}));
|
|
5886
|
+
}
|
|
5887
|
+
if (deadlineMs !== void 0 && Date.now() >= deadlineMs) return yield* new XurlCommandError({
|
|
5888
|
+
message: "xurl OAuth2 fallback timed out",
|
|
5889
|
+
commandArgs: args,
|
|
5890
|
+
rateLimited: false,
|
|
5891
|
+
cause: void 0
|
|
5835
5892
|
});
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
}
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5893
|
+
return yield* runJsonCommandEffect(oauth2ArgsForCandidate(authCandidate, args), scopedOptions).pipe(Effect.catchAll((error) => {
|
|
5894
|
+
if (!primaryUsername) return Effect.fail(error);
|
|
5895
|
+
oauth2CandidateCache.delete(primaryUsername.toLowerCase());
|
|
5896
|
+
const attempted = /* @__PURE__ */ new Set([authCandidate ? `${authCandidate.app ?? "default"}:${authCandidate.username}` : `default:${primaryUsername}`]);
|
|
5897
|
+
if (authCandidate?.username !== primaryUsername) attempted.add(`default:${primaryUsername}`);
|
|
5898
|
+
return lookupOAuth2UsernameForAccountEffect(primaryUsername, attempted, deadlineMs).pipe(Effect.flatMap((fallbackUsername) => {
|
|
5899
|
+
if (!fallbackUsername) return Effect.fail(error);
|
|
5900
|
+
oauth2CandidateCache.set(primaryUsername.toLowerCase(), {
|
|
5901
|
+
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5902
|
+
value: fallbackUsername
|
|
5903
|
+
});
|
|
5904
|
+
return runJsonCommandEffect(oauth2ArgsForCandidate(fallbackUsername, args), scopedOptions);
|
|
5905
|
+
}), Effect.catchAll(() => Effect.fail(error)));
|
|
5906
|
+
}));
|
|
5907
|
+
});
|
|
5908
|
+
var runMutationCommandEffect = Effect.fn("xurl.runMutationCommand")(function* (args) {
|
|
5909
|
+
if (liveWritesDisabled$3()) {
|
|
5910
|
+
if (e2eFakeLiveWritesEnabled$2()) return {
|
|
5853
5911
|
ok: true,
|
|
5854
|
-
output:
|
|
5855
|
-
}
|
|
5912
|
+
output: "e2e fake live write"
|
|
5913
|
+
};
|
|
5914
|
+
return {
|
|
5856
5915
|
ok: false,
|
|
5857
|
-
output:
|
|
5858
|
-
}
|
|
5859
|
-
}
|
|
5860
|
-
|
|
5861
|
-
|
|
5916
|
+
output: "live writes disabled"
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
return yield* runSubprocessEffect({
|
|
5920
|
+
command: "xurl",
|
|
5921
|
+
args
|
|
5922
|
+
}).pipe(Effect.map(({ stdout, stderr }) => ({
|
|
5923
|
+
ok: true,
|
|
5924
|
+
output: stdout || stderr || "ok"
|
|
5925
|
+
})), Effect.catchAll((error) => Effect.succeed({
|
|
5926
|
+
ok: false,
|
|
5927
|
+
output: formatExecError$1(error, "xurl execution failed")
|
|
5928
|
+
})));
|
|
5929
|
+
});
|
|
5930
|
+
var lookupUsersByIdsEffect = Effect.fn("xurl.lookupUsersByIds")((ids) => {
|
|
5862
5931
|
if (ids.length === 0) return Effect.succeed([]);
|
|
5863
5932
|
return runJsonCommandEffect([`/2/users?${new URLSearchParams({
|
|
5864
5933
|
ids: ids.join(","),
|
|
5865
5934
|
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5866
5935
|
}).toString()}`]).pipe(Effect.map((payload) => Array.isArray(payload.data) ? payload.data : []));
|
|
5936
|
+
});
|
|
5937
|
+
function normalizeXListPage(payload) {
|
|
5938
|
+
const data = (Array.isArray(payload.data) ? payload.data : []).map((value) => {
|
|
5939
|
+
const item = value;
|
|
5940
|
+
if (typeof item.id !== "string" || typeof item.name !== "string") return null;
|
|
5941
|
+
return {
|
|
5942
|
+
id: item.id,
|
|
5943
|
+
name: item.name,
|
|
5944
|
+
...typeof item.description === "string" ? { description: item.description } : {},
|
|
5945
|
+
...typeof item.member_count === "number" ? { memberCount: item.member_count } : {},
|
|
5946
|
+
...typeof item.follower_count === "number" ? { followerCount: item.follower_count } : {},
|
|
5947
|
+
...typeof item.private === "boolean" ? { isPrivate: item.private } : {},
|
|
5948
|
+
...typeof item.owner_id === "string" ? { ownerId: item.owner_id } : {},
|
|
5949
|
+
...typeof item.created_at === "string" ? { createdAt: item.created_at } : {},
|
|
5950
|
+
raw: item
|
|
5951
|
+
};
|
|
5952
|
+
}).filter((item) => Boolean(item));
|
|
5953
|
+
const meta = payload.meta && typeof payload.meta === "object" ? payload.meta : {};
|
|
5954
|
+
return {
|
|
5955
|
+
data,
|
|
5956
|
+
meta: {
|
|
5957
|
+
result_count: data.length,
|
|
5958
|
+
...typeof meta.next_token === "string" ? { next_token: meta.next_token } : { next_token: null }
|
|
5959
|
+
}
|
|
5960
|
+
};
|
|
5867
5961
|
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5962
|
+
Effect.fn("xurl.listOwnedXLists")(({ userId, username, maxResults, paginationToken }) => {
|
|
5963
|
+
const query = new URLSearchParams({
|
|
5964
|
+
max_results: String(maxResults),
|
|
5965
|
+
"list.fields": "created_at,description,follower_count,member_count,name,owner_id,private"
|
|
5966
|
+
});
|
|
5967
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5968
|
+
return runOAuth2JsonCommandEffect({
|
|
5969
|
+
args: [`/2/users/${userId}/owned_lists?${query.toString()}`],
|
|
5970
|
+
username
|
|
5971
|
+
}).pipe(Effect.map(normalizeXListPage));
|
|
5972
|
+
});
|
|
5973
|
+
Effect.fn("xurl.listXListMembers")(({ listId, username, maxResults, paginationToken }) => {
|
|
5974
|
+
const query = new URLSearchParams({
|
|
5975
|
+
max_results: String(maxResults),
|
|
5976
|
+
"user.fields": RICH_USER_FIELDS
|
|
5977
|
+
});
|
|
5978
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5979
|
+
return runOAuth2JsonCommandEffect({
|
|
5980
|
+
args: [`/2/lists/${listId}/members?${query.toString()}`],
|
|
5981
|
+
username
|
|
5982
|
+
}).pipe(Effect.map((payload) => ({
|
|
5983
|
+
data: Array.isArray(payload.data) ? payload.data : [],
|
|
5984
|
+
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : {}
|
|
5985
|
+
})));
|
|
5986
|
+
});
|
|
5987
|
+
var lookupUsersByHandlesEffect = Effect.fn("xurl.lookupUsersByHandles")((handles, options = {}) => {
|
|
5872
5988
|
if (handles.length === 0) return Effect.succeed([]);
|
|
5873
5989
|
const args = [`/2/users/by?${new URLSearchParams({
|
|
5874
5990
|
usernames: handles.map((item) => item.replace(/^@/, "")).join(","),
|
|
@@ -5880,114 +5996,72 @@ function lookupUsersByHandlesEffect(handles, options = {}) {
|
|
|
5880
5996
|
options: { signal: options.signal },
|
|
5881
5997
|
useConfiguredCandidate: options.useConfiguredCandidate
|
|
5882
5998
|
}) : runJsonCommandEffect(args, { signal: options.signal })).pipe(Effect.map((payload) => Array.isArray(payload.data) ? payload.data : []));
|
|
5883
|
-
}
|
|
5884
|
-
function lookupUsersByHandles(handles, options = {}) {
|
|
5885
|
-
return runEffectPromise(lookupUsersByHandlesEffect(handles, options));
|
|
5886
|
-
}
|
|
5999
|
+
});
|
|
5887
6000
|
function authenticatedUserFromPayload(payload) {
|
|
5888
6001
|
const data = payload.data;
|
|
5889
6002
|
return data && typeof data === "object" ? data : null;
|
|
5890
6003
|
}
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
args: ["whoami"],
|
|
5897
|
-
username
|
|
5898
|
-
}).pipe(Effect.map(authenticatedUserFromPayload));
|
|
5899
|
-
}
|
|
6004
|
+
var lookupAuthenticatedUserFreshEffect = Effect.fn("xurl.lookupAuthenticatedUserFresh")(() => runJsonCommandEffect(["whoami"]).pipe(Effect.map(authenticatedUserFromPayload)));
|
|
6005
|
+
var lookupAuthenticatedOAuth2UserEffect = Effect.fn("xurl.lookupAuthenticatedOAuth2User")((username) => runOAuth2JsonCommandEffect({
|
|
6006
|
+
args: ["whoami"],
|
|
6007
|
+
username
|
|
6008
|
+
}).pipe(Effect.map(authenticatedUserFromPayload)));
|
|
5900
6009
|
function lookupAuthenticatedUserEffect() {
|
|
5901
|
-
return Effect.
|
|
5902
|
-
const now = Date.now();
|
|
5903
|
-
if (authenticatedUserCache && "value" in authenticatedUserCache && authenticatedUserCache.expiresAt > now) return authenticatedUserCache.value ?? null;
|
|
5904
|
-
if (authenticatedUserCache?.pending) return yield* Effect.tryPromise({
|
|
5905
|
-
try: () => authenticatedUserCache?.pending ?? Promise.resolve(null),
|
|
5906
|
-
catch: normalizeError
|
|
5907
|
-
});
|
|
5908
|
-
const pending = runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
5909
|
-
authenticatedUserCache = {
|
|
5910
|
-
expiresAt: 0,
|
|
5911
|
-
pending
|
|
5912
|
-
};
|
|
5913
|
-
const value = yield* Effect.tryPromise({
|
|
5914
|
-
try: () => pending,
|
|
5915
|
-
catch: normalizeError
|
|
5916
|
-
}).pipe(Effect.catchAll((error) => Effect.sync(() => {
|
|
5917
|
-
authenticatedUserCache = void 0;
|
|
5918
|
-
}).pipe(Effect.flatMap(() => Effect.fail(error)))));
|
|
5919
|
-
authenticatedUserCache = {
|
|
5920
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5921
|
-
value
|
|
5922
|
-
};
|
|
5923
|
-
return value;
|
|
5924
|
-
});
|
|
5925
|
-
}
|
|
5926
|
-
function lookupAuthenticatedUser() {
|
|
5927
|
-
return runEffectPromise(lookupAuthenticatedUserEffect());
|
|
6010
|
+
return cachedAuthenticatedUser.pipe(Effect.tapError(() => invalidateAuthenticatedUser));
|
|
5928
6011
|
}
|
|
5929
6012
|
function lookupAuthenticatedUserFresh() {
|
|
5930
6013
|
return runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
5931
6014
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
if (!user?.id) return yield* Effect.fail(/* @__PURE__ */ new Error(`Could not resolve Twitter user id for @${username}`));
|
|
5938
|
-
return String(user.id);
|
|
5939
|
-
}
|
|
5940
|
-
const user = yield* lookupAuthenticatedUserEffect();
|
|
5941
|
-
if (!user?.id) return yield* Effect.fail(/* @__PURE__ */ new Error("Could not resolve authenticated Twitter user id"));
|
|
6015
|
+
var resolveUserIdEffect = Effect.fn("xurl.resolveUserId")(function* ({ username, userId }) {
|
|
6016
|
+
if (userId) return userId;
|
|
6017
|
+
if (username) {
|
|
6018
|
+
const [user] = yield* lookupUsersByHandlesEffect([username]);
|
|
6019
|
+
if (!user?.id) return yield* Effect.fail(/* @__PURE__ */ new Error(`Could not resolve Twitter user id for @${username}`));
|
|
5942
6020
|
return String(user.id);
|
|
6021
|
+
}
|
|
6022
|
+
const user = yield* lookupAuthenticatedUserEffect();
|
|
6023
|
+
if (!user?.id) return yield* Effect.fail(/* @__PURE__ */ new Error("Could not resolve authenticated Twitter user id"));
|
|
6024
|
+
return String(user.id);
|
|
6025
|
+
});
|
|
6026
|
+
var listMentionsViaXurlEffect = Effect.fn("xurl.listMentions")(function* ({ maxResults, username, userId, paginationToken, sinceId, startTime }) {
|
|
6027
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6028
|
+
username,
|
|
6029
|
+
userId
|
|
5943
6030
|
});
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
});
|
|
5951
|
-
const query = new URLSearchParams({
|
|
5952
|
-
max_results: String(maxResults),
|
|
5953
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
5954
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics",
|
|
5955
|
-
"media.fields": MEDIA_FIELDS,
|
|
5956
|
-
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5957
|
-
});
|
|
5958
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5959
|
-
if (sinceId) query.set("since_id", sinceId);
|
|
5960
|
-
if (startTime) query.set("start_time", startTime);
|
|
5961
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
5962
|
-
args: [`/2/users/${resolvedUserId}/mentions?${query.toString()}`],
|
|
5963
|
-
username
|
|
5964
|
-
}));
|
|
6031
|
+
const query = new URLSearchParams({
|
|
6032
|
+
max_results: String(maxResults),
|
|
6033
|
+
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6034
|
+
"tweet.fields": "created_at,conversation_id,entities,public_metrics",
|
|
6035
|
+
"media.fields": MEDIA_FIELDS,
|
|
6036
|
+
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5965
6037
|
});
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
5979
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
5980
|
-
"media.fields": MEDIA_FIELDS,
|
|
5981
|
-
"user.fields": RICH_USER_FIELDS
|
|
5982
|
-
});
|
|
5983
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5984
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
5985
|
-
args: [`/2/users/${resolvedUserId}/timelines/reverse_chronological?${query.toString()}`],
|
|
5986
|
-
username,
|
|
5987
|
-
options: { timeoutMs }
|
|
5988
|
-
}));
|
|
6038
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6039
|
+
if (sinceId) query.set("since_id", sinceId);
|
|
6040
|
+
if (startTime) query.set("start_time", startTime);
|
|
6041
|
+
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
6042
|
+
args: [`/2/users/${resolvedUserId}/mentions?${query.toString()}`],
|
|
6043
|
+
username
|
|
6044
|
+
}));
|
|
6045
|
+
});
|
|
6046
|
+
var listHomeTimelineViaXurlEffect = Effect.fn("xurl.listHomeTimeline")(function* ({ maxResults, username, userId, paginationToken, timeoutMs }) {
|
|
6047
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6048
|
+
username,
|
|
6049
|
+
userId
|
|
5989
6050
|
});
|
|
5990
|
-
|
|
6051
|
+
const query = new URLSearchParams({
|
|
6052
|
+
max_results: String(maxResults),
|
|
6053
|
+
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6054
|
+
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
6055
|
+
"media.fields": MEDIA_FIELDS,
|
|
6056
|
+
"user.fields": RICH_USER_FIELDS
|
|
6057
|
+
});
|
|
6058
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6059
|
+
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
6060
|
+
args: [`/2/users/${resolvedUserId}/timelines/reverse_chronological?${query.toString()}`],
|
|
6061
|
+
username,
|
|
6062
|
+
options: { timeoutMs }
|
|
6063
|
+
}));
|
|
6064
|
+
});
|
|
5991
6065
|
function toXurlMentionsResponse(payload) {
|
|
5992
6066
|
return {
|
|
5993
6067
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
@@ -5995,45 +6069,37 @@ function toXurlMentionsResponse(payload) {
|
|
|
5995
6069
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
5996
6070
|
};
|
|
5997
6071
|
}
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
userId
|
|
6003
|
-
});
|
|
6004
|
-
const requestMaxResults = capTimelineCollectionMaxResults(collection, maxResults, isPaginatedWalk);
|
|
6005
|
-
const query = new URLSearchParams({
|
|
6006
|
-
max_results: String(requestMaxResults),
|
|
6007
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6008
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
6009
|
-
"media.fields": MEDIA_FIELDS,
|
|
6010
|
-
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
6011
|
-
});
|
|
6012
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6013
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
6014
|
-
args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
|
|
6015
|
-
username
|
|
6016
|
-
}));
|
|
6072
|
+
var listTimelineCollectionViaXurlEffect = Effect.fn("xurl.listTimelineCollection")(function* ({ collection, maxResults, username, userId, isPaginatedWalk = false, paginationToken }) {
|
|
6073
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6074
|
+
username,
|
|
6075
|
+
userId
|
|
6017
6076
|
});
|
|
6018
|
-
|
|
6077
|
+
const requestMaxResults = capTimelineCollectionMaxResults(collection, maxResults, isPaginatedWalk);
|
|
6078
|
+
const query = new URLSearchParams({
|
|
6079
|
+
max_results: String(requestMaxResults),
|
|
6080
|
+
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6081
|
+
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
6082
|
+
"media.fields": MEDIA_FIELDS,
|
|
6083
|
+
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
6084
|
+
});
|
|
6085
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6086
|
+
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
6087
|
+
args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
|
|
6088
|
+
username
|
|
6089
|
+
}));
|
|
6090
|
+
});
|
|
6019
6091
|
function listLikedTweetsViaXurlEffect(options) {
|
|
6020
6092
|
return listTimelineCollectionViaXurlEffect({
|
|
6021
6093
|
...options,
|
|
6022
6094
|
collection: "liked_tweets"
|
|
6023
6095
|
});
|
|
6024
6096
|
}
|
|
6025
|
-
function listLikedTweetsViaXurl(options) {
|
|
6026
|
-
return runEffectPromise(listLikedTweetsViaXurlEffect(options));
|
|
6027
|
-
}
|
|
6028
6097
|
function listBookmarkedTweetsViaXurlEffect(options) {
|
|
6029
6098
|
return listTimelineCollectionViaXurlEffect({
|
|
6030
6099
|
...options,
|
|
6031
6100
|
collection: "bookmarks"
|
|
6032
6101
|
});
|
|
6033
6102
|
}
|
|
6034
|
-
function listBookmarkedTweetsViaXurl(options) {
|
|
6035
|
-
return runEffectPromise(listBookmarkedTweetsViaXurlEffect(options));
|
|
6036
|
-
}
|
|
6037
6103
|
function listDirectMessageEventsViaXurlEffect({ maxResults, username, paginationToken }) {
|
|
6038
6104
|
const query = new URLSearchParams({
|
|
6039
6105
|
max_results: String(maxResults),
|
|
@@ -6052,31 +6118,26 @@ function listDirectMessageEventsViaXurlEffect({ maxResults, username, pagination
|
|
|
6052
6118
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6053
6119
|
})));
|
|
6054
6120
|
}
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
userId
|
|
6060
|
-
});
|
|
6061
|
-
const query = new URLSearchParams({
|
|
6062
|
-
max_results: String(maxResults),
|
|
6063
|
-
"user.fields": "id,username,name,description,verified,protected,public_metrics,profile_image_url,created_at"
|
|
6064
|
-
});
|
|
6065
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6066
|
-
const payload = yield* runOAuth2JsonCommandEffect({
|
|
6067
|
-
args: [`/2/users/${resolvedUserId}/${direction}?${query.toString()}`],
|
|
6068
|
-
username
|
|
6069
|
-
});
|
|
6070
|
-
return {
|
|
6071
|
-
data: Array.isArray(payload.data) ? payload.data : [],
|
|
6072
|
-
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6073
|
-
};
|
|
6121
|
+
var listFollowUsersViaXurlEffect = Effect.fn("xurl.listFollowUsers")(function* ({ direction, maxResults, username, userId, paginationToken }) {
|
|
6122
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6123
|
+
username,
|
|
6124
|
+
userId
|
|
6074
6125
|
});
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
}
|
|
6079
|
-
|
|
6126
|
+
const query = new URLSearchParams({
|
|
6127
|
+
max_results: String(maxResults),
|
|
6128
|
+
"user.fields": "id,username,name,description,verified,protected,public_metrics,profile_image_url,created_at"
|
|
6129
|
+
});
|
|
6130
|
+
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6131
|
+
const payload = yield* runOAuth2JsonCommandEffect({
|
|
6132
|
+
args: [`/2/users/${resolvedUserId}/${direction}?${query.toString()}`],
|
|
6133
|
+
username
|
|
6134
|
+
});
|
|
6135
|
+
return {
|
|
6136
|
+
data: Array.isArray(payload.data) ? payload.data : [],
|
|
6137
|
+
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6138
|
+
};
|
|
6139
|
+
});
|
|
6140
|
+
var listBlockedUsersEffect = Effect.fn("xurl.listBlockedUsers")((userId, paginationToken) => {
|
|
6080
6141
|
const query = new URLSearchParams({
|
|
6081
6142
|
max_results: "100",
|
|
6082
6143
|
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
@@ -6090,11 +6151,8 @@ function listBlockedUsersEffect(userId, paginationToken) {
|
|
|
6090
6151
|
nextToken: typeof meta?.next_token === "string" ? String(meta.next_token) : null
|
|
6091
6152
|
};
|
|
6092
6153
|
}));
|
|
6093
|
-
}
|
|
6094
|
-
|
|
6095
|
-
return runEffectPromise(listBlockedUsersEffect(userId, paginationToken));
|
|
6096
|
-
}
|
|
6097
|
-
function listUserTweetsEffect(userId, { maxResults, paginationToken, excludeRetweets = true, sinceId, untilId, tweetFields, expansions, userFields, mediaFields, auth, username, signal, onAttempt, useConfiguredCandidate }) {
|
|
6154
|
+
});
|
|
6155
|
+
var listUserTweetsEffect = Effect.fn("xurl.listUserTweets")((userId, { maxResults, paginationToken, excludeRetweets = true, sinceId, untilId, tweetFields, expansions, userFields, mediaFields, auth, username, signal, onAttempt, useConfiguredCandidate }) => {
|
|
6098
6156
|
const query = new URLSearchParams({
|
|
6099
6157
|
max_results: String(maxResults),
|
|
6100
6158
|
expansions: MEDIA_EXPANSION,
|
|
@@ -6130,10 +6188,7 @@ function listUserTweetsEffect(userId, { maxResults, paginationToken, excludeRetw
|
|
|
6130
6188
|
...includes ? { includes } : {}
|
|
6131
6189
|
};
|
|
6132
6190
|
}));
|
|
6133
|
-
}
|
|
6134
|
-
function listUserTweets(userId, options) {
|
|
6135
|
-
return runEffectPromise(listUserTweetsEffect(userId, options));
|
|
6136
|
-
}
|
|
6191
|
+
});
|
|
6137
6192
|
function toXurlTweetsResponse(payload) {
|
|
6138
6193
|
return {
|
|
6139
6194
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
@@ -6141,7 +6196,17 @@ function toXurlTweetsResponse(payload) {
|
|
|
6141
6196
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6142
6197
|
};
|
|
6143
6198
|
}
|
|
6144
|
-
|
|
6199
|
+
Effect.fn("xurl.lookupTweetsByIds")((ids) => {
|
|
6200
|
+
if (ids.length === 0) return Effect.succeed({ data: [] });
|
|
6201
|
+
return runJsonCommandEffect([`/2/tweets?${new URLSearchParams({
|
|
6202
|
+
ids: ids.join(","),
|
|
6203
|
+
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6204
|
+
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
6205
|
+
"media.fields": MEDIA_FIELDS,
|
|
6206
|
+
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
6207
|
+
}).toString()}`]).pipe(Effect.map(toXurlTweetsResponse));
|
|
6208
|
+
});
|
|
6209
|
+
var searchRecentByConversationIdEffect = Effect.fn("xurl.searchRecentByConversationId")((conversationId, { maxResults, paginationToken, timeoutMs, auth, username, signal, onAttempt }) => {
|
|
6145
6210
|
const query = new URLSearchParams({
|
|
6146
6211
|
query: `conversation_id:${conversationId}`,
|
|
6147
6212
|
max_results: String(maxResults),
|
|
@@ -6166,8 +6231,8 @@ function searchRecentByConversationIdEffect(conversationId, { maxResults, pagina
|
|
|
6166
6231
|
signal,
|
|
6167
6232
|
onAttempt
|
|
6168
6233
|
})).pipe(Effect.map(toXurlTweetsResponse));
|
|
6169
|
-
}
|
|
6170
|
-
|
|
6234
|
+
});
|
|
6235
|
+
var searchRecentTweetsEffect = Effect.fn("xurl.searchRecentTweets")((searchQuery, { maxResults, paginationToken, startTime, endTime, username, timeoutMs }) => {
|
|
6171
6236
|
const query = new URLSearchParams({
|
|
6172
6237
|
query: searchQuery,
|
|
6173
6238
|
max_results: String(maxResults),
|
|
@@ -6185,8 +6250,8 @@ function searchRecentTweetsEffect(searchQuery, { maxResults, paginationToken, st
|
|
|
6185
6250
|
options: { timeoutMs },
|
|
6186
6251
|
useConfiguredCandidate: false
|
|
6187
6252
|
}).pipe(Effect.map(toXurlTweetsResponse));
|
|
6188
|
-
}
|
|
6189
|
-
|
|
6253
|
+
});
|
|
6254
|
+
var getTweetByIdEffect = Effect.fn("xurl.getTweetById")((id, { timeoutMs } = {}) => {
|
|
6190
6255
|
return runJsonCommandEffect([`/2/tweets/${id}?${new URLSearchParams({
|
|
6191
6256
|
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6192
6257
|
"tweet.fields": THREAD_TWEET_FIELDS,
|
|
@@ -6199,7 +6264,7 @@ function getTweetByIdEffect(id, { timeoutMs } = {}) {
|
|
|
6199
6264
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6200
6265
|
};
|
|
6201
6266
|
}));
|
|
6202
|
-
}
|
|
6267
|
+
});
|
|
6203
6268
|
function postViaXurlEffect(text) {
|
|
6204
6269
|
return runShortcutEffect(["post", text]);
|
|
6205
6270
|
}
|
|
@@ -6226,9 +6291,6 @@ function blockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6226
6291
|
JSON.stringify({ target_user_id: targetUserId })
|
|
6227
6292
|
]);
|
|
6228
6293
|
}
|
|
6229
|
-
function blockUserViaXurl(sourceUserId, targetUserId) {
|
|
6230
|
-
return runEffectPromise(blockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6231
|
-
}
|
|
6232
6294
|
function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6233
6295
|
return runMutationCommandEffect([
|
|
6234
6296
|
"-X",
|
|
@@ -6236,9 +6298,6 @@ function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6236
6298
|
`/2/users/${sourceUserId}/blocking/${targetUserId}`
|
|
6237
6299
|
]);
|
|
6238
6300
|
}
|
|
6239
|
-
function unblockUserViaXurl(sourceUserId, targetUserId) {
|
|
6240
|
-
return runEffectPromise(unblockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6241
|
-
}
|
|
6242
6301
|
function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6243
6302
|
return runMutationCommandEffect([
|
|
6244
6303
|
"-X",
|
|
@@ -6248,9 +6307,6 @@ function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6248
6307
|
JSON.stringify({ target_user_id: targetUserId })
|
|
6249
6308
|
]);
|
|
6250
6309
|
}
|
|
6251
|
-
function muteUserViaXurl(sourceUserId, targetUserId) {
|
|
6252
|
-
return runEffectPromise(muteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6253
|
-
}
|
|
6254
6310
|
function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6255
6311
|
return runMutationCommandEffect([
|
|
6256
6312
|
"-X",
|
|
@@ -6258,44 +6314,35 @@ function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6258
6314
|
`/2/users/${sourceUserId}/muting/${targetUserId}`
|
|
6259
6315
|
]);
|
|
6260
6316
|
}
|
|
6261
|
-
function unmuteUserViaXurl(sourceUserId, targetUserId) {
|
|
6262
|
-
return runEffectPromise(unmuteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6263
|
-
}
|
|
6264
6317
|
//#endregion
|
|
6265
6318
|
//#region src/lib/live-transport-gateway.ts
|
|
6266
|
-
function fromPromise(run) {
|
|
6267
|
-
return Effect.tryPromise({
|
|
6268
|
-
try: run,
|
|
6269
|
-
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
6270
|
-
});
|
|
6271
|
-
}
|
|
6272
6319
|
var liveTransportGateway = {
|
|
6273
6320
|
bird: {
|
|
6274
|
-
getAuthenticatedAccount: () => getAuthenticatedBirdAccountEffect(),
|
|
6275
|
-
listBookmarks: (
|
|
6276
|
-
listDirectMessages: (
|
|
6277
|
-
listFollowUsers: (
|
|
6278
|
-
listHomeTimeline: (
|
|
6279
|
-
listLikes: (
|
|
6280
|
-
listMentions: (
|
|
6281
|
-
listThread: (
|
|
6282
|
-
searchTweets: (
|
|
6321
|
+
getAuthenticatedAccount: (...args) => getAuthenticatedBirdAccountEffect(...args),
|
|
6322
|
+
listBookmarks: (...args) => listBookmarkedTweetsViaBirdEffect(...args),
|
|
6323
|
+
listDirectMessages: (...args) => listDirectMessagesViaBirdEffect(...args),
|
|
6324
|
+
listFollowUsers: (...args) => listFollowUsersViaBirdEffect(...args),
|
|
6325
|
+
listHomeTimeline: (...args) => listHomeTimelineViaBirdEffect(...args),
|
|
6326
|
+
listLikes: (...args) => listLikedTweetsViaBirdEffect(...args),
|
|
6327
|
+
listMentions: (...args) => listMentionsViaBirdEffect(...args),
|
|
6328
|
+
listThread: (...args) => listThreadViaBirdEffect(...args),
|
|
6329
|
+
searchTweets: (...args) => searchTweetsViaBirdEffect(...args)
|
|
6283
6330
|
},
|
|
6284
6331
|
xurl: {
|
|
6285
|
-
getTransportStatus: (...args) =>
|
|
6286
|
-
getTweetById: (
|
|
6287
|
-
listBookmarks: (...args) =>
|
|
6288
|
-
listDirectMessages: (
|
|
6289
|
-
listFollowUsers: (...args) =>
|
|
6290
|
-
listHomeTimeline: (
|
|
6291
|
-
listLikes: (...args) =>
|
|
6292
|
-
listMentions: (...args) =>
|
|
6293
|
-
listUserTweets: (...args) =>
|
|
6294
|
-
lookupAuthenticatedOAuth2User: (
|
|
6295
|
-
lookupAuthenticatedUser: (...args) =>
|
|
6296
|
-
lookupUsersByHandles: (...args) =>
|
|
6297
|
-
searchConversation: (
|
|
6298
|
-
searchRecentTweets: (
|
|
6332
|
+
getTransportStatus: (...args) => getTransportStatusEffect(...args),
|
|
6333
|
+
getTweetById: (...args) => getTweetByIdEffect(...args),
|
|
6334
|
+
listBookmarks: (...args) => listBookmarkedTweetsViaXurlEffect(...args),
|
|
6335
|
+
listDirectMessages: (...args) => listDirectMessageEventsViaXurlEffect(...args),
|
|
6336
|
+
listFollowUsers: (...args) => listFollowUsersViaXurlEffect(...args),
|
|
6337
|
+
listHomeTimeline: (...args) => listHomeTimelineViaXurlEffect(...args),
|
|
6338
|
+
listLikes: (...args) => listLikedTweetsViaXurlEffect(...args),
|
|
6339
|
+
listMentions: (...args) => listMentionsViaXurlEffect(...args),
|
|
6340
|
+
listUserTweets: (...args) => listUserTweetsEffect(...args),
|
|
6341
|
+
lookupAuthenticatedOAuth2User: (...args) => lookupAuthenticatedOAuth2UserEffect(...args),
|
|
6342
|
+
lookupAuthenticatedUser: (...args) => lookupAuthenticatedUserEffect(...args),
|
|
6343
|
+
lookupUsersByHandles: (...args) => lookupUsersByHandlesEffect(...args),
|
|
6344
|
+
searchConversation: (...args) => searchRecentByConversationIdEffect(...args),
|
|
6345
|
+
searchRecentTweets: (...args) => searchRecentTweetsEffect(...args)
|
|
6299
6346
|
}
|
|
6300
6347
|
};
|
|
6301
6348
|
//#endregion
|
|
@@ -6457,8 +6504,8 @@ var MAX_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
|
6457
6504
|
var MAX_AVATAR_DATA_URL_CHARS = MAX_AVATAR_BYTES * 4;
|
|
6458
6505
|
var REMOTE_AVATAR_TIMEOUT_MS = 1e4;
|
|
6459
6506
|
var DEFAULT_AVATAR_PREFETCH_CONCURRENCY = 8;
|
|
6460
|
-
var ALLOWED_REMOTE_AVATAR_HOSTS = new Set(["pbs.twimg.com"]);
|
|
6461
|
-
var RASTER_CONTENT_TYPES = new Set([
|
|
6507
|
+
var ALLOWED_REMOTE_AVATAR_HOSTS = /* @__PURE__ */ new Set(["pbs.twimg.com"]);
|
|
6508
|
+
var RASTER_CONTENT_TYPES = /* @__PURE__ */ new Set([
|
|
6462
6509
|
"image/jpeg",
|
|
6463
6510
|
"image/png",
|
|
6464
6511
|
"image/webp",
|
|
@@ -8256,7 +8303,7 @@ function listMentionsByIds(db, accountId, tweetIds, limit) {
|
|
|
8256
8303
|
}
|
|
8257
8304
|
function mergeMentionThreadIntoLocalStore({ db, accountId, accountHandle, mentionIds, payload, source = "bird", writeThreadContextEdges = false }) {
|
|
8258
8305
|
const usersById = new Map((payload.includes?.users ?? []).map((user) => [user.id, user]));
|
|
8259
|
-
const groups = new Map([
|
|
8306
|
+
const groups = /* @__PURE__ */ new Map([
|
|
8260
8307
|
["mention", []],
|
|
8261
8308
|
["home", []],
|
|
8262
8309
|
["thread", []]
|
|
@@ -8317,7 +8364,7 @@ function fetchParentChainViaXurlEffect({ mention, maxDepth, timeoutMs, deadlineM
|
|
|
8317
8364
|
return Effect.gen(function* () {
|
|
8318
8365
|
const pages = [];
|
|
8319
8366
|
const warnings = [];
|
|
8320
|
-
const seenTweetIds = new Set([mention.id]);
|
|
8367
|
+
const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
|
|
8321
8368
|
let nextParentId = mention.replyToId;
|
|
8322
8369
|
let fallbackDepth = 0;
|
|
8323
8370
|
let generalReadTweets = 0;
|
|
@@ -8363,7 +8410,7 @@ function fetchParentChainViaXurlEffect({ mention, maxDepth, timeoutMs, deadlineM
|
|
|
8363
8410
|
}
|
|
8364
8411
|
function findMissingAncestorId(mention, payload) {
|
|
8365
8412
|
const tweetsById = new Map(payload.data.map((tweet) => [tweet.id, tweet]));
|
|
8366
|
-
const seenTweetIds = new Set([mention.id]);
|
|
8413
|
+
const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
|
|
8367
8414
|
const anchorTweet = tweetsById.get(mention.id) ?? mention.rawTweet;
|
|
8368
8415
|
let nextParentId = anchorTweet ? getReplyToId(anchorTweet) : mention.replyToId;
|
|
8369
8416
|
while (nextParentId) {
|
|
@@ -8855,9 +8902,10 @@ function getTimelineQualityReason(row, lowQualityThreshold) {
|
|
|
8855
8902
|
return "keep:long-text";
|
|
8856
8903
|
}
|
|
8857
8904
|
var RECENT_TIMELINE_EDGE_CANDIDATES = 5e3;
|
|
8858
|
-
|
|
8859
|
-
|
|
8905
|
+
var FTS_DRIVE_FROM_MATCHES_MAX = 1e4;
|
|
8906
|
+
function buildTimelineItemsQuery({ resource, account, listAccountId, listId, search, replyFilter = "all", since, until, untilId, includeReplies = true, qualityFilter = "all", lowQualityThreshold, likedOnly = false, bookmarkedOnly = false, limit = 18 }, ftsMatchCountHint = 0) {
|
|
8860
8907
|
const kind = resource === "mentions" ? "mention" : resource;
|
|
8908
|
+
const cteParams = [];
|
|
8861
8909
|
const params = [];
|
|
8862
8910
|
const normalizedLowQualityThreshold = normalizeLowQualityThreshold(lowQualityThreshold);
|
|
8863
8911
|
const shouldDedupeAcrossAccounts = !account || account === "all";
|
|
@@ -8870,9 +8918,8 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
8870
8918
|
`;
|
|
8871
8919
|
const unwindowedTimelineEdgesCte = timelineEdgesCte;
|
|
8872
8920
|
let usedRecentEdgeWindow = false;
|
|
8873
|
-
let join = "";
|
|
8874
8921
|
let where = "where e.kind = ?";
|
|
8875
|
-
|
|
8922
|
+
const ftsSearch = search?.trim() ? toFtsSearchQuery(search) : "";
|
|
8876
8923
|
const canUseRecentEdgeWindow = !likedOnly && !bookmarkedOnly && !listId && !account && !search?.trim() && replyFilter === "all" && !since?.trim() && !until?.trim() && includeReplies && qualityFilter === "all";
|
|
8877
8924
|
if (likedOnly || bookmarkedOnly) {
|
|
8878
8925
|
if (likedOnly && bookmarkedOnly) timelineEdgesCte = `
|
|
@@ -8895,7 +8942,7 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
8895
8942
|
where kind = ?
|
|
8896
8943
|
)
|
|
8897
8944
|
`;
|
|
8898
|
-
|
|
8945
|
+
cteParams.push(collectionKind);
|
|
8899
8946
|
}
|
|
8900
8947
|
where = "where 1 = 1";
|
|
8901
8948
|
} else if (canUseRecentEdgeWindow) {
|
|
@@ -8914,11 +8961,11 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
8914
8961
|
)
|
|
8915
8962
|
`;
|
|
8916
8963
|
const candidateLimit = Math.max(RECENT_TIMELINE_EDGE_CANDIDATES, limit * 50);
|
|
8917
|
-
|
|
8964
|
+
cteParams.push(kind, candidateLimit);
|
|
8918
8965
|
where = "where e.kind = ?";
|
|
8919
8966
|
params.push(kind);
|
|
8920
8967
|
} else {
|
|
8921
|
-
|
|
8968
|
+
cteParams.push(kind);
|
|
8922
8969
|
where = "where e.kind = ?";
|
|
8923
8970
|
params.push(kind);
|
|
8924
8971
|
}
|
|
@@ -8967,16 +9014,28 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
8967
9014
|
`;
|
|
8968
9015
|
params.push(listAccountId, listId);
|
|
8969
9016
|
}
|
|
8970
|
-
const
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
9017
|
+
const ftsMatchesCte = ftsSearch ? `, fts_matches as materialized (
|
|
9018
|
+
select tweet_id
|
|
9019
|
+
from tweets_fts
|
|
9020
|
+
where tweets_fts.text match ?
|
|
9021
|
+
)` : "";
|
|
9022
|
+
if (ftsSearch) cteParams.push(ftsSearch);
|
|
9023
|
+
const searchDrivenFrom = ftsMatchCountHint > FTS_DRIVE_FROM_MATCHES_MAX ? `tweets t
|
|
9024
|
+
cross join fts_matches on fts_matches.tweet_id = t.id
|
|
9025
|
+
cross join timeline_edges e on e.tweet_id = t.id` : `fts_matches
|
|
9026
|
+
cross join tweets t on t.id = fts_matches.tweet_id
|
|
9027
|
+
cross join timeline_edges e on e.tweet_id = t.id`;
|
|
8977
9028
|
params.push(limit);
|
|
9029
|
+
if (ftsSearch) params.push(limit);
|
|
9030
|
+
const searchSelectionCte = ftsSearch ? `, search_selection as materialized (
|
|
9031
|
+
select t.id as tweet_id, e.account_id, e.kind, e.raw_json
|
|
9032
|
+
from ${searchDrivenFrom}
|
|
9033
|
+
${where}
|
|
9034
|
+
order by t.created_at desc, t.id desc
|
|
9035
|
+
limit ?
|
|
9036
|
+
)` : "";
|
|
8978
9037
|
const buildTimelineSelectSql = (timelineEdgesSql) => `
|
|
8979
|
-
${timelineEdgesSql}
|
|
9038
|
+
${timelineEdgesSql}${ftsMatchesCte}${searchSelectionCte}
|
|
8980
9039
|
select
|
|
8981
9040
|
t.id,
|
|
8982
9041
|
e.account_id,
|
|
@@ -9053,8 +9112,7 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
9053
9112
|
qp.avatar_hue as quoted_avatar_hue,
|
|
9054
9113
|
qp.avatar_url as quoted_avatar_url,
|
|
9055
9114
|
qp.created_at as quoted_profile_created_at
|
|
9056
|
-
|
|
9057
|
-
from timeline_edges e
|
|
9115
|
+
from ${ftsSearch ? "search_selection e" : "timeline_edges e"}
|
|
9058
9116
|
join tweets t on t.id = e.tweet_id
|
|
9059
9117
|
join accounts a on a.id = e.account_id
|
|
9060
9118
|
join profiles p on p.id = t.author_profile_id
|
|
@@ -9062,13 +9120,40 @@ function listTimelineItems({ resource, account, listAccountId, listId, search, r
|
|
|
9062
9120
|
left join profiles rp on rp.id = rt.author_profile_id
|
|
9063
9121
|
left join tweets qt on qt.id = t.quoted_tweet_id
|
|
9064
9122
|
left join profiles qp on qp.id = qt.author_profile_id
|
|
9065
|
-
${
|
|
9066
|
-
${where}
|
|
9123
|
+
${ftsSearch ? "" : where}
|
|
9067
9124
|
order by t.created_at desc, t.id desc
|
|
9068
9125
|
limit ?
|
|
9069
9126
|
`;
|
|
9070
|
-
|
|
9071
|
-
|
|
9127
|
+
return {
|
|
9128
|
+
sql: buildTimelineSelectSql(timelineEdgesCte),
|
|
9129
|
+
params: [...cteParams, ...params],
|
|
9130
|
+
fallbackSql: buildTimelineSelectSql(unwindowedTimelineEdgesCte),
|
|
9131
|
+
fallbackParams: [
|
|
9132
|
+
kind,
|
|
9133
|
+
kind,
|
|
9134
|
+
limit
|
|
9135
|
+
],
|
|
9136
|
+
usedRecentEdgeWindow,
|
|
9137
|
+
ftsSearch
|
|
9138
|
+
};
|
|
9139
|
+
}
|
|
9140
|
+
var SEARCH_SNIPPET_SQL = "snippet(tweets_fts, 1, '<mark>', '</mark>', '...', 16)";
|
|
9141
|
+
function listTimelineItems(query) {
|
|
9142
|
+
const db = getReadDb();
|
|
9143
|
+
const { includeQualityReason = false, lowQualityThreshold, limit = 18 } = query;
|
|
9144
|
+
const normalizedLowQualityThreshold = normalizeLowQualityThreshold(lowQualityThreshold);
|
|
9145
|
+
const ftsSearch = query.search?.trim() ? toFtsSearchQuery(query.search) : "";
|
|
9146
|
+
const plan = buildTimelineItemsQuery(query, ftsSearch ? Number(db.prepare("select count(*) as match_count from tweets_fts where tweets_fts.text match ?").get(ftsSearch).match_count) : 0);
|
|
9147
|
+
let rows = db.prepare(plan.sql).all(...plan.params);
|
|
9148
|
+
if (plan.usedRecentEdgeWindow && rows.length < limit) rows = db.prepare(plan.fallbackSql).all(...plan.fallbackParams);
|
|
9149
|
+
if (plan.ftsSearch && rows.length > 0) {
|
|
9150
|
+
const snippetRows = db.prepare(`select tweet_id, ${SEARCH_SNIPPET_SQL} as search_snippet
|
|
9151
|
+
from tweets_fts
|
|
9152
|
+
where tweets_fts.text match ?
|
|
9153
|
+
and tweet_id in (${rows.map(() => "?").join(",")})`).all(plan.ftsSearch, ...rows.map((row) => String(row.id)));
|
|
9154
|
+
const snippetByTweetId = new Map(snippetRows.map((row) => [String(row.tweet_id), row.search_snippet]));
|
|
9155
|
+
for (const row of rows) row.search_snippet = snippetByTweetId.get(String(row.id));
|
|
9156
|
+
}
|
|
9072
9157
|
const urlExpansionCache = /* @__PURE__ */ new Map();
|
|
9073
9158
|
const profileByHandleCache = /* @__PURE__ */ new Map();
|
|
9074
9159
|
return rows.map((row) => {
|
|
@@ -11476,7 +11561,7 @@ function runModeEffect(mode, options) {
|
|
|
11476
11561
|
}
|
|
11477
11562
|
function combineTweetSearchResults(left, right, limit) {
|
|
11478
11563
|
if (left.ok && right.ok) {
|
|
11479
|
-
const tweetIds = [
|
|
11564
|
+
const tweetIds = [.../* @__PURE__ */ new Set([...left.tweetIds, ...right.tweetIds])].slice(0, limit);
|
|
11480
11565
|
const liveSources = new Set([left.source, right.source].filter((source) => source !== "cache"));
|
|
11481
11566
|
return {
|
|
11482
11567
|
ok: true,
|
|
@@ -12279,7 +12364,7 @@ function resolveProfilesForHandlesEffect(handles, options = {}) {
|
|
|
12279
12364
|
unresolved = [];
|
|
12280
12365
|
}
|
|
12281
12366
|
if (unresolved.length > 0 && xurlFallback) {
|
|
12282
|
-
const xurlResult = yield*
|
|
12367
|
+
const xurlResult = yield* lookupUsersByHandlesEffect(unresolved).pipe(Effect.map((users) => ({
|
|
12283
12368
|
ok: true,
|
|
12284
12369
|
users
|
|
12285
12370
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -12378,6 +12463,8 @@ function toError$9(error) {
|
|
|
12378
12463
|
return error instanceof Error ? error : new Error(String(error));
|
|
12379
12464
|
}
|
|
12380
12465
|
function isXurlRateLimitError(error) {
|
|
12466
|
+
const tagged = error;
|
|
12467
|
+
if (tagged._tag === "XurlCommandError" && tagged.rateLimited === true) return true;
|
|
12381
12468
|
return error.message.includes("Too Many Requests") || error.message.includes("\"status\":429") || /\b429\b/.test(error.message);
|
|
12382
12469
|
}
|
|
12383
12470
|
function tryProfileSync(try_) {
|
|
@@ -13083,7 +13170,7 @@ var Route$11 = createFileRoute("/api/profile-analysis")({ server: { handlers: {
|
|
|
13083
13170
|
//#region src/lib/link-insights.ts
|
|
13084
13171
|
var DEFAULT_LIMIT$1 = 30;
|
|
13085
13172
|
var DEFAULT_COMMENTS_LIMIT = 8;
|
|
13086
|
-
var TRACKING_PARAMS = new Set([
|
|
13173
|
+
var TRACKING_PARAMS = /* @__PURE__ */ new Set([
|
|
13087
13174
|
"fbclid",
|
|
13088
13175
|
"gclid",
|
|
13089
13176
|
"igshid",
|
|
@@ -13102,17 +13189,17 @@ var VIDEO_HOST_SUFFIXES = [
|
|
|
13102
13189
|
"tiktok.com",
|
|
13103
13190
|
"loom.com"
|
|
13104
13191
|
];
|
|
13105
|
-
var VIDEO_EXACT_HOSTS = new Set([
|
|
13192
|
+
var VIDEO_EXACT_HOSTS = /* @__PURE__ */ new Set([
|
|
13106
13193
|
"youtu.be",
|
|
13107
13194
|
"clips.twitch.tv",
|
|
13108
13195
|
"vm.tiktok.com"
|
|
13109
13196
|
]);
|
|
13110
13197
|
var EXCLUDED_HOST_SUFFIXES = ["x.com", "twitter.com"];
|
|
13111
|
-
var EXCLUDED_EXACT_HOSTS = new Set(["t.co"]);
|
|
13198
|
+
var EXCLUDED_EXACT_HOSTS = /* @__PURE__ */ new Set(["t.co"]);
|
|
13112
13199
|
var URL_WHITESPACE = /\s+/g;
|
|
13113
13200
|
var RAW_URL_PATTERN = /https?:\/\/[^\s<>"'`]+/g;
|
|
13114
13201
|
var SQL_URL_EXPRESSION = "lower(coalesce(nullif(e.final_url, ''), nullif(e.expanded_url, ''), e.short_url))";
|
|
13115
|
-
var TITLE_SEGMENT_STOPWORDS = new Set([
|
|
13202
|
+
var TITLE_SEGMENT_STOPWORDS = /* @__PURE__ */ new Set([
|
|
13116
13203
|
"a",
|
|
13117
13204
|
"about",
|
|
13118
13205
|
"blog",
|
|
@@ -13125,7 +13212,7 @@ var TITLE_SEGMENT_STOPWORDS = new Set([
|
|
|
13125
13212
|
"story",
|
|
13126
13213
|
"watch"
|
|
13127
13214
|
]);
|
|
13128
|
-
var TITLE_WORD_OVERRIDES = new Map([
|
|
13215
|
+
var TITLE_WORD_OVERRIDES = /* @__PURE__ */ new Map([
|
|
13129
13216
|
["ai", "AI"],
|
|
13130
13217
|
["api", "API"],
|
|
13131
13218
|
["codex", "Codex"],
|
|
@@ -13136,7 +13223,7 @@ var TITLE_WORD_OVERRIDES = new Map([
|
|
|
13136
13223
|
["url", "URL"],
|
|
13137
13224
|
["wwdc", "WWDC"]
|
|
13138
13225
|
]);
|
|
13139
|
-
var TITLE_SMALL_WORDS = new Set([
|
|
13226
|
+
var TITLE_SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
13140
13227
|
"a",
|
|
13141
13228
|
"an",
|
|
13142
13229
|
"and",
|
|
@@ -13285,7 +13372,7 @@ function stripUrls(text, urls) {
|
|
|
13285
13372
|
let output = text;
|
|
13286
13373
|
for (const url of urls) {
|
|
13287
13374
|
if (!url) continue;
|
|
13288
|
-
const variants = new Set([url]);
|
|
13375
|
+
const variants = /* @__PURE__ */ new Set([url]);
|
|
13289
13376
|
try {
|
|
13290
13377
|
const parsed = new URL(url);
|
|
13291
13378
|
variants.add(parsed.href);
|
|
@@ -13672,9 +13759,9 @@ function getLinkInsights(query = {}) {
|
|
|
13672
13759
|
kind: rowKind,
|
|
13673
13760
|
lastSeenAt: row.created_at,
|
|
13674
13761
|
mentions: [mention],
|
|
13675
|
-
seenMentions: new Set([mention.id]),
|
|
13676
|
-
sharers: new Set([sharerKey]),
|
|
13677
|
-
sharerProfiles: mention.sharedBy ? new Map([[mention.sharedBy.id, mention.sharedBy]]) : /* @__PURE__ */ new Map(),
|
|
13762
|
+
seenMentions: /* @__PURE__ */ new Set([mention.id]),
|
|
13763
|
+
sharers: /* @__PURE__ */ new Set([sharerKey]),
|
|
13764
|
+
sharerProfiles: mention.sharedBy ? /* @__PURE__ */ new Map([[mention.sharedBy.id, mention.sharedBy]]) : /* @__PURE__ */ new Map(),
|
|
13678
13765
|
shareCount: 1,
|
|
13679
13766
|
title: row.title,
|
|
13680
13767
|
topSharer: mention.sharedBy ?? null,
|
|
@@ -14540,7 +14627,7 @@ var LETTER_PATTERN = /[a-z]/i;
|
|
|
14540
14627
|
var URL_OR_HANDLE_PATTERN = /(https?:\/\/|@|[A-Z0-9._%+-]+@[A-Z0-9.-]+)/i;
|
|
14541
14628
|
var MONEY_PATTERN = /\$\s?\d/;
|
|
14542
14629
|
var EMOJI_BLOCK_PATTERN = /[\u{1f1e6}-\u{1f1ff}\u{1f300}-\u{1faff}\u{2600}-\u{27bf}]/gu;
|
|
14543
|
-
var GENERIC_LOCATIONS = new Set([
|
|
14630
|
+
var GENERIC_LOCATIONS = /* @__PURE__ */ new Set([
|
|
14544
14631
|
"earth",
|
|
14545
14632
|
"everywhere",
|
|
14546
14633
|
"internet",
|
|
@@ -14558,7 +14645,7 @@ var GENERIC_LOCATIONS = new Set([
|
|
|
14558
14645
|
"world",
|
|
14559
14646
|
"world wide web"
|
|
14560
14647
|
]);
|
|
14561
|
-
var DROP_WORDS = new Set([
|
|
14648
|
+
var DROP_WORDS = /* @__PURE__ */ new Set([
|
|
14562
14649
|
"a",
|
|
14563
14650
|
"an",
|
|
14564
14651
|
"and",
|
|
@@ -15112,7 +15199,7 @@ function upsertUrlExpansion(db, item) {
|
|
|
15112
15199
|
var FETCH_TIMEOUT_MS = 12e3;
|
|
15113
15200
|
var MAX_HTML_CHARS = 2e6;
|
|
15114
15201
|
var MAX_REDIRECTS = 4;
|
|
15115
|
-
var NO_BODY_STATUS_CODES = new Set([
|
|
15202
|
+
var NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
15116
15203
|
204,
|
|
15117
15204
|
205,
|
|
15118
15205
|
304
|
|
@@ -15182,15 +15269,6 @@ function isInjectedFetchAllowed() {
|
|
|
15182
15269
|
function stripAddressBrackets(value) {
|
|
15183
15270
|
return value.replace(/^\[|\]$/g, "");
|
|
15184
15271
|
}
|
|
15185
|
-
function withTimeout(promise, ms) {
|
|
15186
|
-
let timer = null;
|
|
15187
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
15188
|
-
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Link preview request timed out")), ms);
|
|
15189
|
-
});
|
|
15190
|
-
return Promise.race([promise, timeout]).finally(() => {
|
|
15191
|
-
if (timer) clearTimeout(timer);
|
|
15192
|
-
});
|
|
15193
|
-
}
|
|
15194
15272
|
async function resolvePublicAddresses(hostname) {
|
|
15195
15273
|
const normalized = stripAddressBrackets(hostname);
|
|
15196
15274
|
if (net.isIP(normalized)) return [normalized];
|
|
@@ -15203,19 +15281,24 @@ function validateResolvedAddresses(addresses) {
|
|
|
15203
15281
|
if (addresses.length === 0) throw new Error("Link preview host did not resolve");
|
|
15204
15282
|
if (addresses.some(isBlockedAddress)) throw new Error("Link preview URL resolves to a private address");
|
|
15205
15283
|
}
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
family
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15284
|
+
var resolveSafeAddressesEffect = Effect.fn("linkPreview.resolveSafeAddresses")((hostname, resolveHost, timeoutMs) => tryPromise(() => resolveHost(stripAddressBrackets(hostname))).pipe(Effect.timeoutFail({
|
|
15285
|
+
duration: timeoutMs,
|
|
15286
|
+
onTimeout: () => /* @__PURE__ */ new Error("Link preview request timed out")
|
|
15287
|
+
}), Effect.flatMap((addresses) => Effect.try({
|
|
15288
|
+
try: () => {
|
|
15289
|
+
validateResolvedAddresses(addresses);
|
|
15290
|
+
return addresses.map((address) => {
|
|
15291
|
+
const normalized = stripAddressBrackets(address);
|
|
15292
|
+
const family = net.isIP(normalized);
|
|
15293
|
+
if (family !== 4 && family !== 6) throw new Error("Link preview host resolved to an invalid address");
|
|
15294
|
+
return {
|
|
15295
|
+
address: normalized,
|
|
15296
|
+
family
|
|
15297
|
+
};
|
|
15298
|
+
});
|
|
15299
|
+
},
|
|
15300
|
+
catch: (error) => error
|
|
15301
|
+
}))));
|
|
15219
15302
|
function headersFromIncoming(headers) {
|
|
15220
15303
|
const result = new Headers();
|
|
15221
15304
|
for (const [key, value] of Object.entries(headers)) if (Array.isArray(value)) for (const item of value) result.append(key, item);
|
|
@@ -15311,7 +15394,7 @@ function nodeSafeFetch(url, options) {
|
|
|
15311
15394
|
return fetchAddress(address, Math.max(1, Math.ceil(remainingMs / remainingAddresses)));
|
|
15312
15395
|
}), Promise.reject(/* @__PURE__ */ new Error("Link preview host did not resolve")));
|
|
15313
15396
|
}
|
|
15314
|
-
|
|
15397
|
+
var safePreviewFetchEffect = Effect.fn("linkPreview.safePreviewFetch")(function* (url, options) {
|
|
15315
15398
|
const resolveHost = options.resolveHost ?? (options.fetchImpl ? null : (hostname) => resolvePublicAddresses(hostname));
|
|
15316
15399
|
const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
15317
15400
|
const method = options.method ?? "GET";
|
|
@@ -15322,43 +15405,39 @@ function safePreviewFetchEffect(url, options) {
|
|
|
15322
15405
|
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
15323
15406
|
"accept-language": "en-US,en;q=0.9"
|
|
15324
15407
|
};
|
|
15325
|
-
return Effect.
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
}
|
|
15359
|
-
return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15360
|
-
});
|
|
15361
|
-
}
|
|
15408
|
+
if (options.fetchImpl && !isInjectedFetchAllowed()) return yield* Effect.fail(/* @__PURE__ */ new Error("Custom link preview fetch is only available in tests"));
|
|
15409
|
+
let currentUrl = url;
|
|
15410
|
+
for (let redirect = 0; redirect <= MAX_REDIRECTS; redirect += 1) {
|
|
15411
|
+
if (Date.now() >= deadline) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview request timed out"));
|
|
15412
|
+
const parsed = yield* Effect.try({
|
|
15413
|
+
try: () => assertSafePreviewUrl(currentUrl),
|
|
15414
|
+
catch: (error) => error
|
|
15415
|
+
});
|
|
15416
|
+
if (options.fetchImpl && resolveHost) yield* resolveSafeAddressesEffect(parsed.hostname, resolveHost, remainingTimeoutMs());
|
|
15417
|
+
if (Date.now() >= deadline) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview request timed out"));
|
|
15418
|
+
const response = options.fetchImpl ? yield* tryPromise(() => options.fetchImpl?.(parsed.toString(), {
|
|
15419
|
+
headers,
|
|
15420
|
+
method,
|
|
15421
|
+
redirect: "manual",
|
|
15422
|
+
signal: AbortSignal.timeout(remainingTimeoutMs())
|
|
15423
|
+
})) : yield* resolveSafeAddressesEffect(parsed.hostname, options.resolveHost ?? resolvePublicAddresses, remainingTimeoutMs()).pipe(Effect.filterOrFail(() => Date.now() < deadline, () => /* @__PURE__ */ new Error("Link preview request timed out")), Effect.flatMap((addresses) => tryPromise(() => nodeSafeFetch(parsed, {
|
|
15424
|
+
addresses,
|
|
15425
|
+
headers,
|
|
15426
|
+
method,
|
|
15427
|
+
timeoutMs: remainingTimeoutMs()
|
|
15428
|
+
}))));
|
|
15429
|
+
if (response.status < 300 || response.status >= 400) return response;
|
|
15430
|
+
const location = response.headers.get("location");
|
|
15431
|
+
if (!location) return response;
|
|
15432
|
+
yield* cancelResponseBodyEffect(response);
|
|
15433
|
+
if (redirect === MAX_REDIRECTS) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15434
|
+
currentUrl = yield* Effect.try({
|
|
15435
|
+
try: () => new URL(location, parsed).toString(),
|
|
15436
|
+
catch: (error) => error
|
|
15437
|
+
});
|
|
15438
|
+
}
|
|
15439
|
+
return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15440
|
+
});
|
|
15362
15441
|
function readResponseTextEffect(response) {
|
|
15363
15442
|
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
15364
15443
|
if (Number.isFinite(contentLength) && contentLength > MAX_HTML_CHARS) return tryPromise(() => response.body?.cancel() ?? Promise.resolve()).pipe(Effect.flatMap(() => Effect.fail(/* @__PURE__ */ new Error("Link preview response is too large"))));
|
|
@@ -15422,39 +15501,37 @@ function extractLinkPreviewMetadata(html, url) {
|
|
|
15422
15501
|
siteName
|
|
15423
15502
|
};
|
|
15424
15503
|
}
|
|
15425
|
-
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15453
|
-
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
})));
|
|
15457
|
-
}
|
|
15504
|
+
var fetchLinkPreviewMetadataEffect = Effect.fn("linkPreview.fetchMetadata")(function* (url, _options = {}) {
|
|
15505
|
+
const response = yield* safePreviewFetchEffect(url, _options);
|
|
15506
|
+
const finalUrl = response.url || url;
|
|
15507
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
15508
|
+
if (!response.ok) {
|
|
15509
|
+
yield* cancelResponseBodyEffect(response);
|
|
15510
|
+
return yield* Effect.fail(/* @__PURE__ */ new Error(`HTTP ${response.status}`));
|
|
15511
|
+
}
|
|
15512
|
+
if (contentType.toLowerCase().startsWith("image/")) {
|
|
15513
|
+
yield* cancelResponseBodyEffect(response);
|
|
15514
|
+
return {
|
|
15515
|
+
url: finalUrl,
|
|
15516
|
+
title: hostLabel(finalUrl),
|
|
15517
|
+
description: null,
|
|
15518
|
+
imageUrl: finalUrl,
|
|
15519
|
+
siteName: hostLabel(finalUrl)
|
|
15520
|
+
};
|
|
15521
|
+
}
|
|
15522
|
+
const content = yield* readResponseTextEffect(response);
|
|
15523
|
+
return yield* Effect.try({
|
|
15524
|
+
try: () => extractLinkPreviewMetadata(content.slice(0, MAX_HTML_CHARS), finalUrl),
|
|
15525
|
+
catch: (error) => error
|
|
15526
|
+
});
|
|
15527
|
+
}, (effect, url) => Effect.catchAll(effect, (error) => Effect.succeed({
|
|
15528
|
+
url,
|
|
15529
|
+
title: hostLabel(url),
|
|
15530
|
+
description: null,
|
|
15531
|
+
imageUrl: youtubeThumbnail(url),
|
|
15532
|
+
siteName: hostLabel(url),
|
|
15533
|
+
error: error instanceof Error ? error.message : String(error)
|
|
15534
|
+
})));
|
|
15458
15535
|
function readCachedPreview(db, url, shortUrl) {
|
|
15459
15536
|
return db.prepare(`
|
|
15460
15537
|
select short_url, expanded_url, final_url, status, title, description,
|
|
@@ -15508,16 +15585,14 @@ function persistPreview(db, url, shortUrl, cached, preview) {
|
|
|
15508
15585
|
updatedAt: now
|
|
15509
15586
|
}));
|
|
15510
15587
|
}
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
});
|
|
15520
|
-
}
|
|
15588
|
+
var getOrFetchLinkPreviewEffect = Effect.fn("linkPreview.getOrFetch")(function* (url, options = {}) {
|
|
15589
|
+
const db = getNativeDb({ seedDemoData: false });
|
|
15590
|
+
const cached = readCachedPreview(db, url, options.shortUrl);
|
|
15591
|
+
if (cached && hasUsefulPreview(cached) && !options.refresh) return rowToPreview(cached);
|
|
15592
|
+
const preview = yield* fetchLinkPreviewMetadataEffect(cached?.final_url || cached?.expanded_url || url, options);
|
|
15593
|
+
persistPreview(db, url, options.shortUrl, cached, preview);
|
|
15594
|
+
return preview;
|
|
15595
|
+
});
|
|
15521
15596
|
({ ...__test__$1 });
|
|
15522
15597
|
//#endregion
|
|
15523
15598
|
//#region src/routes/api/link-preview.tsx
|
|
@@ -16116,9 +16191,6 @@ function readBirdStatusViaBirdEffect(query) {
|
|
|
16116
16191
|
]);
|
|
16117
16192
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
16118
16193
|
}
|
|
16119
|
-
function readBirdStatusViaBird(query) {
|
|
16120
|
-
return runEffectPromise(readBirdStatusViaBirdEffect(query));
|
|
16121
|
-
}
|
|
16122
16194
|
function toBirdLookupUser(payload) {
|
|
16123
16195
|
const user = payload.user && typeof payload.user === "object" ? payload.user : null;
|
|
16124
16196
|
if (!user) throw new Error("bird user payload missing user object");
|
|
@@ -16164,9 +16236,6 @@ function lookupProfileViaBirdEffect(query) {
|
|
|
16164
16236
|
});
|
|
16165
16237
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
16166
16238
|
}
|
|
16167
|
-
function lookupProfileViaBird(query) {
|
|
16168
|
-
return runEffectPromise(lookupProfileViaBirdEffect(query));
|
|
16169
|
-
}
|
|
16170
16239
|
function runVerifiedBirdMutationEffect({ action, query, verifyField, expectedValue }) {
|
|
16171
16240
|
return Effect.gen(function* () {
|
|
16172
16241
|
const safeQuery = yield* Effect.try({
|
|
@@ -16215,9 +16284,6 @@ function blockUserViaBirdEffect(query) {
|
|
|
16215
16284
|
expectedValue: true
|
|
16216
16285
|
});
|
|
16217
16286
|
}
|
|
16218
|
-
function blockUserViaBird(query) {
|
|
16219
|
-
return runEffectPromise(blockUserViaBirdEffect(query));
|
|
16220
|
-
}
|
|
16221
16287
|
function unblockUserViaBirdEffect(query) {
|
|
16222
16288
|
return runVerifiedBirdMutationEffect({
|
|
16223
16289
|
action: "unblock",
|
|
@@ -16226,9 +16292,6 @@ function unblockUserViaBirdEffect(query) {
|
|
|
16226
16292
|
expectedValue: false
|
|
16227
16293
|
});
|
|
16228
16294
|
}
|
|
16229
|
-
function unblockUserViaBird(query) {
|
|
16230
|
-
return runEffectPromise(unblockUserViaBirdEffect(query));
|
|
16231
|
-
}
|
|
16232
16295
|
function muteUserViaBirdEffect(query) {
|
|
16233
16296
|
return runVerifiedBirdMutationEffect({
|
|
16234
16297
|
action: "mute",
|
|
@@ -16237,9 +16300,6 @@ function muteUserViaBirdEffect(query) {
|
|
|
16237
16300
|
expectedValue: true
|
|
16238
16301
|
});
|
|
16239
16302
|
}
|
|
16240
|
-
function muteUserViaBird(query) {
|
|
16241
|
-
return runEffectPromise(muteUserViaBirdEffect(query));
|
|
16242
|
-
}
|
|
16243
16303
|
function unmuteUserViaBirdEffect(query) {
|
|
16244
16304
|
return runVerifiedBirdMutationEffect({
|
|
16245
16305
|
action: "unmute",
|
|
@@ -16248,9 +16308,6 @@ function unmuteUserViaBirdEffect(query) {
|
|
|
16248
16308
|
expectedValue: false
|
|
16249
16309
|
});
|
|
16250
16310
|
}
|
|
16251
|
-
function unmuteUserViaBird(query) {
|
|
16252
|
-
return runEffectPromise(unmuteUserViaBirdEffect(query));
|
|
16253
|
-
}
|
|
16254
16311
|
//#endregion
|
|
16255
16312
|
//#region src/lib/moderation-target.ts
|
|
16256
16313
|
function toError$4(error) {
|
|
@@ -16306,7 +16363,7 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
16306
16363
|
if (local && !local.profile.id.startsWith("profile_group_") && local.externalUserId) return local;
|
|
16307
16364
|
let user;
|
|
16308
16365
|
let lastError;
|
|
16309
|
-
const birdResult = yield*
|
|
16366
|
+
const birdResult = yield* lookupProfileViaBirdEffect(local?.profile.handle ?? normalizedQuery).pipe(Effect.map((value) => ({
|
|
16310
16367
|
ok: true,
|
|
16311
16368
|
value
|
|
16312
16369
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -16316,7 +16373,7 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
16316
16373
|
if (birdResult.ok) user = birdResult.value ?? void 0;
|
|
16317
16374
|
else lastError = birdResult.error;
|
|
16318
16375
|
if (!user) {
|
|
16319
|
-
const xurlResult = yield*
|
|
16376
|
+
const xurlResult = yield* (/^\d+$/.test(normalizedQuery) ? lookupUsersByIdsEffect([normalizedQuery]) : lookupUsersByHandlesEffect([local?.profile.handle ?? normalizedQuery])).pipe(Effect.map((value) => ({
|
|
16320
16377
|
ok: true,
|
|
16321
16378
|
value
|
|
16322
16379
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -16363,7 +16420,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
16363
16420
|
return Effect.gen(function* () {
|
|
16364
16421
|
if (!expectedAccount) return null;
|
|
16365
16422
|
if (liveWritesDisabled$1()) return null;
|
|
16366
|
-
const sourceUser = yield*
|
|
16423
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
16367
16424
|
const sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
16368
16425
|
const sourceUsername = sourceUser && typeof sourceUser.username === "string" ? profileHandleKey(sourceUser.username) : "";
|
|
16369
16426
|
const expectedExternalUserId = expectedAccount.externalUserId?.trim() ?? "";
|
|
@@ -16379,7 +16436,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
16379
16436
|
function runBirdActionEffect(action, query) {
|
|
16380
16437
|
return Effect.gen(function* () {
|
|
16381
16438
|
return {
|
|
16382
|
-
...yield*
|
|
16439
|
+
...yield* action === "block" ? blockUserViaBirdEffect(query) : action === "unblock" ? unblockUserViaBirdEffect(query) : action === "mute" ? muteUserViaBirdEffect(query) : unmuteUserViaBirdEffect(query),
|
|
16383
16440
|
transport: "bird"
|
|
16384
16441
|
};
|
|
16385
16442
|
});
|
|
@@ -16402,7 +16459,7 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
16402
16459
|
};
|
|
16403
16460
|
let sourceUserId = verifiedSourceUserId ?? "";
|
|
16404
16461
|
if (!sourceUserId) {
|
|
16405
|
-
const sourceUser = yield*
|
|
16462
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
16406
16463
|
sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
16407
16464
|
}
|
|
16408
16465
|
if (!sourceUserId) return {
|
|
@@ -16410,12 +16467,12 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
16410
16467
|
output: "xurl authenticated user unavailable",
|
|
16411
16468
|
transport: "xurl"
|
|
16412
16469
|
};
|
|
16413
|
-
const result = yield*
|
|
16470
|
+
const result = yield* action === "block" ? blockUserViaXurlEffect(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurlEffect(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurlEffect(sourceUserId, targetUserId) : unmuteUserViaXurlEffect(sourceUserId, targetUserId);
|
|
16414
16471
|
if (!result.ok) return {
|
|
16415
16472
|
...result,
|
|
16416
16473
|
transport: "xurl"
|
|
16417
16474
|
};
|
|
16418
|
-
const status = yield*
|
|
16475
|
+
const status = yield* readBirdStatusViaBirdEffect(query);
|
|
16419
16476
|
const { field: verifyField, expected: expectedValue } = getVerifyExpectation(action);
|
|
16420
16477
|
const actualValue = status && typeof status[verifyField] === "boolean" ? Boolean(status[verifyField]) : null;
|
|
16421
16478
|
if (actualValue === null) return {
|
|
@@ -16819,7 +16876,7 @@ function syncBlocksEffect(accountId) {
|
|
|
16819
16876
|
}
|
|
16820
16877
|
};
|
|
16821
16878
|
return yield* Effect.gen(function* () {
|
|
16822
|
-
const me = yield*
|
|
16879
|
+
const me = yield* lookupAuthenticatedUserFreshEffect();
|
|
16823
16880
|
const sourceUserId = typeof me?.id === "string" && me.id.length > 0 ? me.id : null;
|
|
16824
16881
|
const sourceUsername = typeof me?.username === "string" ? me.username.replace(/^@/, "") : "";
|
|
16825
16882
|
const accountExternalUserId = account?.external_user_id?.trim() ?? "";
|
|
@@ -16857,7 +16914,7 @@ function syncBlocksEffect(accountId) {
|
|
|
16857
16914
|
let pageCount = 0;
|
|
16858
16915
|
let completed = false;
|
|
16859
16916
|
do {
|
|
16860
|
-
const page = yield*
|
|
16917
|
+
const page = yield* listBlockedUsersEffect(sourceUserId, nextToken ?? void 0);
|
|
16861
16918
|
const pageProfileIds = yield* databaseWriteEffect((writeDb) => {
|
|
16862
16919
|
return page.items.map((user) => {
|
|
16863
16920
|
const resolved = upsertProfileFromXUser(writeDb, user);
|