birdclaw 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -0
- package/dist/cli/birdclaw.js +1297 -1075
- 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-CAvZOdfA.js → router-B874XqWN.js} +759 -697
- 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",
|
|
@@ -2546,6 +2546,10 @@ var Route$17 = createFileRoute("/api/xurl-rate-limits")({ server: { handlers: {
|
|
|
2546
2546
|
})) } } });
|
|
2547
2547
|
//#endregion
|
|
2548
2548
|
//#region src/lib/backup-table-codecs.ts
|
|
2549
|
+
var BACKUP_SHARD_PART_PATTERN = /\.part-\d{4,}\.jsonl$/u;
|
|
2550
|
+
function logicalBackupShardPath(relativePath) {
|
|
2551
|
+
return relativePath.replace(BACKUP_SHARD_PART_PATTERN, ".jsonl");
|
|
2552
|
+
}
|
|
2549
2553
|
function fixedShard(relativePath, countKey) {
|
|
2550
2554
|
return {
|
|
2551
2555
|
shardPath: () => relativePath,
|
|
@@ -2561,7 +2565,7 @@ function yearFromTimestamp(value) {
|
|
|
2561
2565
|
function pathLeaf(relativePath) {
|
|
2562
2566
|
return relativePath.split("/").at(-1) ?? "";
|
|
2563
2567
|
}
|
|
2564
|
-
var JSON_URL_KEYS = new Set([
|
|
2568
|
+
var JSON_URL_KEYS = /* @__PURE__ */ new Set([
|
|
2565
2569
|
"url",
|
|
2566
2570
|
"expandedUrl",
|
|
2567
2571
|
"expanded_url",
|
|
@@ -3514,7 +3518,7 @@ function adaptLegacyTweetState(schemaVersion, tweets, collections, timelineEdges
|
|
|
3514
3518
|
const nextTimelineEdges = [...timelineEdges];
|
|
3515
3519
|
const collectionKeys = new Set(collections.map((row) => `${String(row.account_id)}\0${String(row.tweet_id)}\0${String(row.kind)}`));
|
|
3516
3520
|
const edgeKeys = new Set(timelineEdges.map((row) => `${String(row.account_id)}\0${String(row.tweet_id)}\0${String(row.kind)}`));
|
|
3517
|
-
const edgeKinds = new Set([
|
|
3521
|
+
const edgeKinds = /* @__PURE__ */ new Set([
|
|
3518
3522
|
"home",
|
|
3519
3523
|
"mention",
|
|
3520
3524
|
"authored",
|
|
@@ -3567,7 +3571,8 @@ function adaptLegacyTweetState(schemaVersion, tweets, collections, timelineEdges
|
|
|
3567
3571
|
};
|
|
3568
3572
|
}
|
|
3569
3573
|
function backupCodecForPath(relativePath, codecs = BACKUP_TABLE_CODECS) {
|
|
3570
|
-
const
|
|
3574
|
+
const logicalPath = logicalBackupShardPath(relativePath);
|
|
3575
|
+
const matches = codecs.filter((codec) => codec.matchesPath(logicalPath));
|
|
3571
3576
|
if (matches.length !== 1) throw new Error(matches.length === 0 ? `No backup codec owns path: ${relativePath}` : `Multiple backup codecs own path: ${relativePath}`);
|
|
3572
3577
|
return matches[0];
|
|
3573
3578
|
}
|
|
@@ -3590,7 +3595,7 @@ function countBackupFiles(files, codecs = BACKUP_TABLE_CODECS) {
|
|
|
3590
3595
|
const counts = {};
|
|
3591
3596
|
for (const file of files) {
|
|
3592
3597
|
if (!file.path.startsWith("data/")) continue;
|
|
3593
|
-
const key = backupCodecForPath(file.path, codecs).countKey(file.path);
|
|
3598
|
+
const key = backupCodecForPath(file.path, codecs).countKey(logicalBackupShardPath(file.path));
|
|
3594
3599
|
counts[key] = (counts[key] ?? 0) + file.rows;
|
|
3595
3600
|
}
|
|
3596
3601
|
return counts;
|
|
@@ -3996,8 +4001,9 @@ function runSubprocessEffect(options) {
|
|
|
3996
4001
|
}
|
|
3997
4002
|
//#endregion
|
|
3998
4003
|
//#region src/lib/backup.ts
|
|
3999
|
-
var BACKUP_SCHEMA_VERSION =
|
|
4004
|
+
var BACKUP_SCHEMA_VERSION = 4;
|
|
4000
4005
|
var MIN_SUPPORTED_BACKUP_SCHEMA_VERSION = 1;
|
|
4006
|
+
var DEFAULT_MAX_BACKUP_SHARD_BYTES = 48 * 1024 * 1024;
|
|
4001
4007
|
var MANIFEST_PATH = "manifest.json";
|
|
4002
4008
|
var DATA_DIR = "data";
|
|
4003
4009
|
var GITATTRIBUTES_PATH = ".gitattributes";
|
|
@@ -4080,6 +4086,40 @@ function getExportRowSets(db) {
|
|
|
4080
4086
|
function buildShards(db) {
|
|
4081
4087
|
return buildBackupShardsFromRowSets(getExportRowSets(db));
|
|
4082
4088
|
}
|
|
4089
|
+
function normalizeMaxBackupShardBytes(value) {
|
|
4090
|
+
const maxBytes = value ?? DEFAULT_MAX_BACKUP_SHARD_BYTES;
|
|
4091
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) throw new Error("Backup shard byte limit must be a positive integer");
|
|
4092
|
+
return maxBytes;
|
|
4093
|
+
}
|
|
4094
|
+
function partPath(relativePath, partNumber) {
|
|
4095
|
+
if (!relativePath.endsWith(".jsonl")) throw new Error(`Backup shard path must end in .jsonl: ${relativePath}`);
|
|
4096
|
+
return relativePath.replace(/\.jsonl$/u, `.part-${String(partNumber).padStart(4, "0")}.jsonl`);
|
|
4097
|
+
}
|
|
4098
|
+
function splitJsonlShard(relativePath, rows, maxBytes) {
|
|
4099
|
+
const parts = [];
|
|
4100
|
+
let currentRows = [];
|
|
4101
|
+
let currentBytes = 0;
|
|
4102
|
+
for (const [index, row] of rows.entries()) {
|
|
4103
|
+
const rowBytes = Buffer.byteLength(jsonlStringify(row)) + 1;
|
|
4104
|
+
if (rowBytes > maxBytes) throw new Error(`Backup row exceeds shard byte limit: ${relativePath}:${index + 1} is ${rowBytes} bytes (limit ${maxBytes})`);
|
|
4105
|
+
if (currentRows.length > 0 && currentBytes + rowBytes > maxBytes) {
|
|
4106
|
+
parts.push(currentRows);
|
|
4107
|
+
currentRows = [];
|
|
4108
|
+
currentBytes = 0;
|
|
4109
|
+
}
|
|
4110
|
+
currentRows.push(row);
|
|
4111
|
+
currentBytes += rowBytes;
|
|
4112
|
+
}
|
|
4113
|
+
if (currentRows.length > 0) parts.push(currentRows);
|
|
4114
|
+
if (parts.length <= 1) return [{
|
|
4115
|
+
relativePath,
|
|
4116
|
+
rows: parts[0] ?? []
|
|
4117
|
+
}];
|
|
4118
|
+
return parts.map((partRows, index) => ({
|
|
4119
|
+
relativePath: partPath(relativePath, index + 1),
|
|
4120
|
+
rows: partRows
|
|
4121
|
+
}));
|
|
4122
|
+
}
|
|
4083
4123
|
function writeJsonlFileEffect(repoPath, relativePath, rows) {
|
|
4084
4124
|
return Effect.gen(function* () {
|
|
4085
4125
|
const fullPath = yield* trySync$10(() => resolveBackupFilePath(repoPath, relativePath));
|
|
@@ -4165,6 +4205,7 @@ data/follow_events.jsonl
|
|
|
4165
4205
|
\`\`\`
|
|
4166
4206
|
|
|
4167
4207
|
Tweets are sharded by creation year. Collection-only tweets whose creation date is unknown live in \`data/tweets/unknown.jsonl\`. Timeline edges keep account-scoped home/mention membership separate from canonical tweet content. DMs are sharded by year and keep \`conversation_id\` in each row.
|
|
4208
|
+
Logical shards larger than 48 MiB are split into deterministic \`.part-0001.jsonl\` files so ordinary Git hosting remains usable without Git LFS.
|
|
4168
4209
|
The links shard stores expanded short URLs and their source tweet/DM occurrences so linked-tweet search can be rebuilt without re-expanding every \`t.co\` URL.
|
|
4169
4210
|
|
|
4170
4211
|
Never commit live tokens, browser cookies, raw SQLite WAL/SHM sidecars, or temporary cache files here.
|
|
@@ -4398,7 +4439,7 @@ function pullBackupGitRepoEffect(repoPath) {
|
|
|
4398
4439
|
]).pipe(Effect.as(true), Effect.catchAll(() => Effect.succeed(false)))));
|
|
4399
4440
|
});
|
|
4400
4441
|
}
|
|
4401
|
-
function exportBackupEffect({ repoPath, db, commit = false, push = false, message = "archive: update birdclaw backup", validate = true }) {
|
|
4442
|
+
function exportBackupEffect({ repoPath, db, commit = false, push = false, message = "archive: update birdclaw backup", validate = true, maxShardBytes }) {
|
|
4402
4443
|
return Effect.gen(function* () {
|
|
4403
4444
|
const resolvedRepoPath = yield* trySync$10(() => path.resolve(repoPath));
|
|
4404
4445
|
const database = db ?? (yield* trySync$10(() => getNativeDb({ seedDemoData: false })));
|
|
@@ -4407,10 +4448,12 @@ function exportBackupEffect({ repoPath, db, commit = false, push = false, messag
|
|
|
4407
4448
|
if (!repoStat.isDirectory() || repoStat.isSymbolicLink()) return yield* Effect.fail(/* @__PURE__ */ new Error("Backup repository path must be a real directory"));
|
|
4408
4449
|
yield* ensureBackupGitattributesEffect(resolvedRepoPath);
|
|
4409
4450
|
yield* ensureBackupReadmeEffect(resolvedRepoPath);
|
|
4451
|
+
const maxBytes = yield* trySync$10(() => normalizeMaxBackupShardBytes(maxShardBytes));
|
|
4410
4452
|
const shards = yield* trySync$10(() => buildShards(database));
|
|
4411
4453
|
const shardEntries = yield* trySync$10(() => [...shards.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
4412
|
-
const
|
|
4413
|
-
const
|
|
4454
|
+
const shardParts = yield* trySync$10(() => shardEntries.flatMap(([relativePath, rows]) => splitJsonlShard(relativePath, rows, maxBytes)));
|
|
4455
|
+
const expectedPaths = yield* trySync$10(() => new Set(shardParts.map(({ relativePath }) => relativePath)));
|
|
4456
|
+
const files = yield* Effect.forEach(shardParts, ({ relativePath, rows }) => writeJsonlFileEffect(resolvedRepoPath, relativePath, rows), { concurrency: 1 });
|
|
4414
4457
|
yield* removeStaleBackupFilesEffect(resolvedRepoPath, expectedPaths);
|
|
4415
4458
|
const counts = yield* trySync$10(() => countBackupFiles(files));
|
|
4416
4459
|
const backupHash = yield* trySync$10(() => computeBackupHash(files));
|
|
@@ -4526,7 +4569,7 @@ function readBackupImportRowsEffect(resolvedRepoPath, manifest) {
|
|
|
4526
4569
|
});
|
|
4527
4570
|
}
|
|
4528
4571
|
function rowsForManifestPath(manifest, predicate) {
|
|
4529
|
-
return manifest.files.map((file) => file.path).filter(predicate).sort();
|
|
4572
|
+
return manifest.files.map((file) => file.path).filter((relativePath) => predicate(logicalBackupShardPath(relativePath))).sort();
|
|
4530
4573
|
}
|
|
4531
4574
|
function importBackupEffect({ repoPath, db: providedDb, validate = true, mode = "merge" }) {
|
|
4532
4575
|
return Effect.gen(function* () {
|
|
@@ -4901,7 +4944,7 @@ function validateBackupEffect(repoPath) {
|
|
|
4901
4944
|
file,
|
|
4902
4945
|
errors: fileErrors
|
|
4903
4946
|
};
|
|
4904
|
-
}), { concurrency:
|
|
4947
|
+
}), { concurrency: 1 });
|
|
4905
4948
|
const files = [];
|
|
4906
4949
|
for (const result of results) {
|
|
4907
4950
|
errors.push(...result.errors);
|
|
@@ -5043,33 +5086,62 @@ function makeBirdStdoutTempEffect() {
|
|
|
5043
5086
|
force: true
|
|
5044
5087
|
})));
|
|
5045
5088
|
}
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
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
|
+
})));
|
|
5073
5145
|
function runBirdTweetJsonCommandEffect(args, timeoutMs) {
|
|
5074
5146
|
return runBirdJsonCommandEffect$1([...args, "--json-full"], timeoutMs).pipe(Effect.catchAll((error) => {
|
|
5075
5147
|
if (!isUnsupportedBirdOptionError(error, "--json-full")) return Effect.fail(error);
|
|
@@ -5081,6 +5153,13 @@ function getBirdTweetItems(payload, command) {
|
|
|
5081
5153
|
if (payload && typeof payload === "object" && Array.isArray(payload.tweets)) return payload.tweets;
|
|
5082
5154
|
throw new Error(`bird ${command} returned unexpected JSON`);
|
|
5083
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
|
+
}
|
|
5084
5163
|
function toMediaEntities(media) {
|
|
5085
5164
|
if (!Array.isArray(media) || media.length === 0) return;
|
|
5086
5165
|
return { urls: media.filter((item) => typeof item?.url === "string" && item.url.length > 0).map((item, index) => ({
|
|
@@ -5216,29 +5295,31 @@ function normalizeBirdTweetsPayloadEffect(payload, command) {
|
|
|
5216
5295
|
catch: (error) => error
|
|
5217
5296
|
});
|
|
5218
5297
|
}
|
|
5219
|
-
function
|
|
5220
|
-
return Effect.
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
"-n",
|
|
5224
|
-
String(maxResults)
|
|
5225
|
-
])), "mentions");
|
|
5226
|
-
});
|
|
5227
|
-
}
|
|
5228
|
-
function listTweetsViaBirdCommandEffect({ command, maxResults, all, maxPages }) {
|
|
5229
|
-
return Effect.gen(function* () {
|
|
5230
|
-
const args = [
|
|
5231
|
-
command,
|
|
5232
|
-
"-n",
|
|
5233
|
-
String(maxResults)
|
|
5234
|
-
];
|
|
5235
|
-
if (all) {
|
|
5236
|
-
args.push("--all");
|
|
5237
|
-
if (maxPages !== void 0) args.push("--max-pages", String(maxPages));
|
|
5238
|
-
}
|
|
5239
|
-
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
|
|
5240
5302
|
});
|
|
5241
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
|
+
});
|
|
5242
5323
|
function listLikedTweetsViaBirdEffect(options) {
|
|
5243
5324
|
return listTweetsViaBirdCommandEffect({
|
|
5244
5325
|
command: "likes",
|
|
@@ -5251,30 +5332,32 @@ function listBookmarkedTweetsViaBirdEffect(options) {
|
|
|
5251
5332
|
...options
|
|
5252
5333
|
});
|
|
5253
5334
|
}
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
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
|
+
});
|
|
5278
5361
|
function normalizeBirdFollowUsers(payload, command, maxResults) {
|
|
5279
5362
|
const rawPayload = payload;
|
|
5280
5363
|
const users = Array.isArray(rawPayload) ? rawPayload : rawPayload.users;
|
|
@@ -5298,28 +5381,81 @@ function normalizeBirdFollowUsersEffect(payload, command, maxResults) {
|
|
|
5298
5381
|
catch: (error) => error
|
|
5299
5382
|
});
|
|
5300
5383
|
}
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
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
|
+
};
|
|
5314
5422
|
}
|
|
5315
|
-
function
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
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
|
|
5321
5433
|
});
|
|
5322
|
-
}
|
|
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
|
+
});
|
|
5323
5459
|
function normalizeBirdDmsPayloadEffect(payload) {
|
|
5324
5460
|
return Effect.try({
|
|
5325
5461
|
try: () => {
|
|
@@ -5345,51 +5481,60 @@ function parseBirdWhoami(stdout) {
|
|
|
5345
5481
|
...name ? { name } : {}
|
|
5346
5482
|
};
|
|
5347
5483
|
}
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
catch: (error) => error
|
|
5354
|
-
});
|
|
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
|
|
5355
5489
|
});
|
|
5356
|
-
}
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
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") {
|
|
5366
5512
|
if (allPages) args.push("--all-pages");
|
|
5367
5513
|
else if (typeof maxPages === "number") args.push("--max-pages", String(maxPages));
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
}
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
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([
|
|
5377
5530
|
"user",
|
|
5378
5531
|
target,
|
|
5379
5532
|
"--json",
|
|
5380
|
-
"--
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
target,
|
|
5386
|
-
"--json",
|
|
5387
|
-
"--count",
|
|
5388
|
-
"1"
|
|
5389
|
-
]);
|
|
5390
|
-
})))).user);
|
|
5391
|
-
});
|
|
5392
|
-
}
|
|
5533
|
+
"--count",
|
|
5534
|
+
"1"
|
|
5535
|
+
]);
|
|
5536
|
+
})))).user);
|
|
5537
|
+
});
|
|
5393
5538
|
function toXurlMentionUser(user) {
|
|
5394
5539
|
if (!user?.id || !user.username) return null;
|
|
5395
5540
|
return {
|
|
@@ -5411,7 +5556,7 @@ function toXurlMentionUser(user) {
|
|
|
5411
5556
|
}
|
|
5412
5557
|
};
|
|
5413
5558
|
}
|
|
5414
|
-
|
|
5559
|
+
var lookupProfilesViaBirdEffect = Effect.fn("bird.lookupProfiles")((usernameOrIds) => {
|
|
5415
5560
|
const targets = Array.from(new Set(usernameOrIds.map((target) => target.trim().replace(/^@/, "")).filter((target) => target.length > 0)));
|
|
5416
5561
|
if (targets.length === 0) return Effect.succeed([]);
|
|
5417
5562
|
return runBirdJsonCommandEffect$1([
|
|
@@ -5443,7 +5588,7 @@ function lookupProfilesViaBirdEffect(usernameOrIds) {
|
|
|
5443
5588
|
error: lookupError instanceof Error ? lookupError.message : String(lookupError)
|
|
5444
5589
|
}))), { concurrency: "unbounded" });
|
|
5445
5590
|
}));
|
|
5446
|
-
}
|
|
5591
|
+
});
|
|
5447
5592
|
//#endregion
|
|
5448
5593
|
//#region src/lib/xurl.ts
|
|
5449
5594
|
var TRANSPORT_STATUS_TTL_MS = 5 * 6e4;
|
|
@@ -5456,8 +5601,8 @@ var RICH_USER_FIELDS = "description,entities,location,public_metrics,profile_ima
|
|
|
5456
5601
|
var DM_EVENT_FIELDS = "attachments,created_at,dm_conversation_id,entities,event_type,id,participant_ids,referenced_tweets,sender_id,text";
|
|
5457
5602
|
var THREAD_TWEET_FIELDS = "created_at,conversation_id,entities,public_metrics,referenced_tweets,in_reply_to_user_id,attachments";
|
|
5458
5603
|
var BOOKMARKS_MAX_RESULTS_CAP = 90;
|
|
5459
|
-
var
|
|
5460
|
-
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)));
|
|
5461
5606
|
var oauth2CandidateCache = /* @__PURE__ */ new Map();
|
|
5462
5607
|
function liveWritesDisabled$3() {
|
|
5463
5608
|
return process.env.BIRDCLAW_DISABLE_LIVE_WRITES === "1";
|
|
@@ -5479,8 +5624,15 @@ function formatExecError$1(error, fallback) {
|
|
|
5479
5624
|
if ("stderr" in error && typeof error.stderr === "string" && error.stderr.trim().length > 0) parts.push(stripAnsi$1(error.stderr).trim());
|
|
5480
5625
|
return parts.join("\n");
|
|
5481
5626
|
}
|
|
5482
|
-
|
|
5483
|
-
|
|
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
|
+
});
|
|
5484
5636
|
}
|
|
5485
5637
|
function normalizeError(error) {
|
|
5486
5638
|
return error instanceof Error ? error : new Error(String(error));
|
|
@@ -5496,11 +5648,11 @@ function parseErrorPayload(error) {
|
|
|
5496
5648
|
return null;
|
|
5497
5649
|
}
|
|
5498
5650
|
}
|
|
5499
|
-
function
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
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);
|
|
5504
5656
|
}
|
|
5505
5657
|
function emitJsonCommandAttempt(options, attempt) {
|
|
5506
5658
|
try {
|
|
@@ -5519,93 +5671,60 @@ function hasXurlEffect() {
|
|
|
5519
5671
|
function isUnauthenticatedXurlStatus(status) {
|
|
5520
5672
|
return /no apps registered|no authenticated user|not authenticated|not logged in/i.test(status);
|
|
5521
5673
|
}
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
if (isUnauthenticatedXurlStatus(rawStatus)) return {
|
|
5535
|
-
installed: true,
|
|
5536
|
-
availableTransport: "local",
|
|
5537
|
-
statusText: "xurl installed but not authenticated. local (bird) mode active.",
|
|
5538
|
-
rawStatus
|
|
5539
|
-
};
|
|
5540
|
-
return {
|
|
5541
|
-
installed: true,
|
|
5542
|
-
availableTransport: "xurl",
|
|
5543
|
-
statusText: "xurl available",
|
|
5544
|
-
rawStatus
|
|
5545
|
-
};
|
|
5546
|
-
}), 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 {
|
|
5547
5686
|
installed: true,
|
|
5548
5687
|
availableTransport: "local",
|
|
5549
|
-
statusText:
|
|
5550
|
-
|
|
5551
|
-
});
|
|
5552
|
-
}
|
|
5553
|
-
function getTransportStatusEffect() {
|
|
5554
|
-
return Effect.gen(function* () {
|
|
5555
|
-
const now = Date.now();
|
|
5556
|
-
if (transportStatusCache?.value && transportStatusCache.expiresAt > now) return transportStatusCache.value;
|
|
5557
|
-
if (transportStatusCache?.pending) {
|
|
5558
|
-
const status = yield* Effect.tryPromise({
|
|
5559
|
-
try: () => transportStatusCache?.pending ?? Promise.resolve(void 0),
|
|
5560
|
-
catch: normalizeError
|
|
5561
|
-
});
|
|
5562
|
-
if (status) return status;
|
|
5563
|
-
}
|
|
5564
|
-
const pending = runEffectPromise(readTransportStatusEffect());
|
|
5565
|
-
transportStatusCache = {
|
|
5566
|
-
expiresAt: 0,
|
|
5567
|
-
pending
|
|
5688
|
+
statusText: "xurl installed but not authenticated. local (bird) mode active.",
|
|
5689
|
+
rawStatus
|
|
5568
5690
|
};
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
}).pipe(Effect.flatMap(() => Effect.fail(error)))));
|
|
5575
|
-
transportStatusCache = {
|
|
5576
|
-
expiresAt: Date.now() + TRANSPORT_STATUS_TTL_MS,
|
|
5577
|
-
value: status
|
|
5691
|
+
return {
|
|
5692
|
+
installed: true,
|
|
5693
|
+
availableTransport: "xurl",
|
|
5694
|
+
statusText: "xurl available",
|
|
5695
|
+
rawStatus
|
|
5578
5696
|
};
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
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;
|
|
5584
5705
|
}
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
if (
|
|
5588
|
-
if (e2eFakeLiveWritesEnabled$2()) return {
|
|
5589
|
-
ok: true,
|
|
5590
|
-
output: "e2e fake live write"
|
|
5591
|
-
};
|
|
5592
|
-
return {
|
|
5593
|
-
ok: false,
|
|
5594
|
-
output: "live writes disabled"
|
|
5595
|
-
};
|
|
5596
|
-
}
|
|
5597
|
-
return yield* runSubprocessEffect({
|
|
5598
|
-
command: "xurl",
|
|
5599
|
-
args
|
|
5600
|
-
}).pipe(Effect.map(({ stdout, stderr }) => ({
|
|
5706
|
+
var runShortcutEffect = Effect.fn("xurl.runShortcut")(function* (args) {
|
|
5707
|
+
if (liveWritesDisabled$3()) {
|
|
5708
|
+
if (e2eFakeLiveWritesEnabled$2()) return {
|
|
5601
5709
|
ok: true,
|
|
5602
|
-
output:
|
|
5603
|
-
}
|
|
5710
|
+
output: "e2e fake live write"
|
|
5711
|
+
};
|
|
5712
|
+
return {
|
|
5604
5713
|
ok: false,
|
|
5605
|
-
output:
|
|
5606
|
-
}
|
|
5607
|
-
}
|
|
5608
|
-
|
|
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
|
+
});
|
|
5609
5728
|
function execXurlJsonEffect(args, timeoutMs, signal) {
|
|
5610
5729
|
return runSubprocessEffect({
|
|
5611
5730
|
command: "xurl",
|
|
@@ -5636,35 +5755,31 @@ function execXurlTextEffect(args, deadlineMs) {
|
|
|
5636
5755
|
function parseJsonPayloadEffect(stdout, args) {
|
|
5637
5756
|
return Effect.try({
|
|
5638
5757
|
try: () => JSON.parse(stdout),
|
|
5639
|
-
catch: (error) =>
|
|
5758
|
+
catch: (error) => toXurlCommandError(error, args)
|
|
5640
5759
|
});
|
|
5641
5760
|
}
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
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, {
|
|
5646
5770
|
args,
|
|
5647
5771
|
attempt,
|
|
5648
|
-
status: "
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
const remainingMs = deadlineMs ? Math.max(0, deadlineMs - Date.now()) : void 0;
|
|
5660
|
-
if (remainingMs !== void 0 && retryDelayMs >= remainingMs) return Effect.fail(formatXurlCommandError(error, args));
|
|
5661
|
-
return Effect.sleep(retryDelayMs).pipe(Effect.flatMap(() => runJsonCommandEffect(args, {
|
|
5662
|
-
...options,
|
|
5663
|
-
deadlineMs
|
|
5664
|
-
}, attempt + 1)));
|
|
5665
|
-
}));
|
|
5666
|
-
});
|
|
5667
|
-
}
|
|
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
|
+
});
|
|
5668
5783
|
function cleanXurlUsernameLabel(username) {
|
|
5669
5784
|
const label = username?.trim().replace(/^@/, "");
|
|
5670
5785
|
return label && label !== "-" && label !== "–" && label !== "(none)" && label.toLowerCase() !== "none" && label.toLowerCase() !== "unknown" && /^[^\s]{1,128}$/.test(label) ? label : void 0;
|
|
@@ -5701,29 +5816,25 @@ function parseOAuth2UsernamesFromStatus(rawStatus) {
|
|
|
5701
5816
|
}
|
|
5702
5817
|
return usernames;
|
|
5703
5818
|
}
|
|
5704
|
-
|
|
5705
|
-
return execXurlTextEffect(["auth", "status"], deadlineMs).pipe(Effect.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)), Effect.catchAll(() => Effect.succeed([])));
|
|
5706
|
-
}
|
|
5819
|
+
var readOAuth2UsernameCandidatesEffect = Effect.fn("xurl.readOAuth2UsernameCandidates")((deadlineMs) => execXurlTextEffect(["auth", "status"], deadlineMs).pipe(Effect.map(({ stdout }) => parseOAuth2UsernamesFromStatus(stdout)), Effect.catchAll(() => Effect.succeed([]))));
|
|
5707
5820
|
function readXurlOAuth2AccountsEffect() {
|
|
5708
5821
|
return readOAuth2UsernameCandidatesEffect().pipe(Effect.map((candidates) => candidates.map((candidate) => ({
|
|
5709
5822
|
...candidate.app ? { app: candidate.app } : {},
|
|
5710
5823
|
...candidate.username ? { username: candidate.username } : {}
|
|
5711
5824
|
}))));
|
|
5712
5825
|
}
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
});
|
|
5726
|
-
}
|
|
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
|
+
});
|
|
5727
5838
|
function oauth2ArgsForCandidate(candidate, args) {
|
|
5728
5839
|
return [
|
|
5729
5840
|
...candidate?.app ? ["--app", candidate.app] : [],
|
|
@@ -5744,88 +5855,136 @@ function configuredOAuth2Candidate(primaryUsername) {
|
|
|
5744
5855
|
...effectiveUsername ? { username: effectiveUsername } : {}
|
|
5745
5856
|
};
|
|
5746
5857
|
}
|
|
5747
|
-
|
|
5858
|
+
var runOAuth2JsonCommandEffect = Effect.fn("xurl.runOAuth2JsonCommand")(function* ({ args, username, options, useConfiguredCandidate = true }) {
|
|
5748
5859
|
const primaryUsername = cleanXurlUsernameLabel(username);
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
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 };
|
|
5762
5877
|
else {
|
|
5763
|
-
const
|
|
5764
|
-
|
|
5765
|
-
if (primaryCandidates.length === 1) authCandidate = primaryCandidates[0];
|
|
5766
|
-
else if (primaryCandidates.length > 1) authCandidate = (yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates)) ?? primaryCandidates[0] ?? { username: primaryUsername };
|
|
5767
|
-
else {
|
|
5768
|
-
const fallbackUsername = yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates);
|
|
5769
|
-
if (fallbackUsername) authCandidate = fallbackUsername;
|
|
5770
|
-
}
|
|
5771
|
-
if (authCandidate) oauth2CandidateCache.set(cacheKey, {
|
|
5772
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5773
|
-
value: authCandidate
|
|
5774
|
-
});
|
|
5878
|
+
const fallbackUsername = yield* lookupOAuth2UsernameForAccountEffect(primaryUsername, /* @__PURE__ */ new Set(), deadlineMs, candidates);
|
|
5879
|
+
if (fallbackUsername) authCandidate = fallbackUsername;
|
|
5775
5880
|
}
|
|
5881
|
+
if (authCandidate) oauth2CandidateCache.set(cacheKey, {
|
|
5882
|
+
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5883
|
+
value: authCandidate
|
|
5884
|
+
});
|
|
5776
5885
|
}
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
return lookupOAuth2UsernameForAccountEffect(primaryUsername, attempted, deadlineMs).pipe(Effect.flatMap((fallbackUsername) => {
|
|
5784
|
-
if (!fallbackUsername) return Effect.fail(error);
|
|
5785
|
-
oauth2CandidateCache.set(primaryUsername.toLowerCase(), {
|
|
5786
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5787
|
-
value: fallbackUsername
|
|
5788
|
-
});
|
|
5789
|
-
return runJsonCommandEffect(oauth2ArgsForCandidate(fallbackUsername, args), scopedOptions);
|
|
5790
|
-
}), Effect.catchAll(() => Effect.fail(error)));
|
|
5791
|
-
}));
|
|
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
|
|
5792
5892
|
});
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
}
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
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 {
|
|
5810
5911
|
ok: true,
|
|
5811
|
-
output:
|
|
5812
|
-
}
|
|
5912
|
+
output: "e2e fake live write"
|
|
5913
|
+
};
|
|
5914
|
+
return {
|
|
5813
5915
|
ok: false,
|
|
5814
|
-
output:
|
|
5815
|
-
}
|
|
5816
|
-
}
|
|
5817
|
-
|
|
5818
|
-
|
|
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) => {
|
|
5819
5931
|
if (ids.length === 0) return Effect.succeed([]);
|
|
5820
5932
|
return runJsonCommandEffect([`/2/users?${new URLSearchParams({
|
|
5821
5933
|
ids: ids.join(","),
|
|
5822
5934
|
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5823
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
|
+
};
|
|
5824
5961
|
}
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
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 = {}) => {
|
|
5829
5988
|
if (handles.length === 0) return Effect.succeed([]);
|
|
5830
5989
|
const args = [`/2/users/by?${new URLSearchParams({
|
|
5831
5990
|
usernames: handles.map((item) => item.replace(/^@/, "")).join(","),
|
|
@@ -5837,114 +5996,72 @@ function lookupUsersByHandlesEffect(handles, options = {}) {
|
|
|
5837
5996
|
options: { signal: options.signal },
|
|
5838
5997
|
useConfiguredCandidate: options.useConfiguredCandidate
|
|
5839
5998
|
}) : runJsonCommandEffect(args, { signal: options.signal })).pipe(Effect.map((payload) => Array.isArray(payload.data) ? payload.data : []));
|
|
5840
|
-
}
|
|
5841
|
-
function lookupUsersByHandles(handles, options = {}) {
|
|
5842
|
-
return runEffectPromise(lookupUsersByHandlesEffect(handles, options));
|
|
5843
|
-
}
|
|
5999
|
+
});
|
|
5844
6000
|
function authenticatedUserFromPayload(payload) {
|
|
5845
6001
|
const data = payload.data;
|
|
5846
6002
|
return data && typeof data === "object" ? data : null;
|
|
5847
6003
|
}
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
args: ["whoami"],
|
|
5854
|
-
username
|
|
5855
|
-
}).pipe(Effect.map(authenticatedUserFromPayload));
|
|
5856
|
-
}
|
|
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)));
|
|
5857
6009
|
function lookupAuthenticatedUserEffect() {
|
|
5858
|
-
return Effect.
|
|
5859
|
-
const now = Date.now();
|
|
5860
|
-
if (authenticatedUserCache && "value" in authenticatedUserCache && authenticatedUserCache.expiresAt > now) return authenticatedUserCache.value ?? null;
|
|
5861
|
-
if (authenticatedUserCache?.pending) return yield* Effect.tryPromise({
|
|
5862
|
-
try: () => authenticatedUserCache?.pending ?? Promise.resolve(null),
|
|
5863
|
-
catch: normalizeError
|
|
5864
|
-
});
|
|
5865
|
-
const pending = runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
5866
|
-
authenticatedUserCache = {
|
|
5867
|
-
expiresAt: 0,
|
|
5868
|
-
pending
|
|
5869
|
-
};
|
|
5870
|
-
const value = yield* Effect.tryPromise({
|
|
5871
|
-
try: () => pending,
|
|
5872
|
-
catch: normalizeError
|
|
5873
|
-
}).pipe(Effect.catchAll((error) => Effect.sync(() => {
|
|
5874
|
-
authenticatedUserCache = void 0;
|
|
5875
|
-
}).pipe(Effect.flatMap(() => Effect.fail(error)))));
|
|
5876
|
-
authenticatedUserCache = {
|
|
5877
|
-
expiresAt: Date.now() + AUTHENTICATED_USER_TTL_MS,
|
|
5878
|
-
value
|
|
5879
|
-
};
|
|
5880
|
-
return value;
|
|
5881
|
-
});
|
|
5882
|
-
}
|
|
5883
|
-
function lookupAuthenticatedUser() {
|
|
5884
|
-
return runEffectPromise(lookupAuthenticatedUserEffect());
|
|
6010
|
+
return cachedAuthenticatedUser.pipe(Effect.tapError(() => invalidateAuthenticatedUser));
|
|
5885
6011
|
}
|
|
5886
6012
|
function lookupAuthenticatedUserFresh() {
|
|
5887
6013
|
return runEffectPromise(lookupAuthenticatedUserFreshEffect());
|
|
5888
6014
|
}
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
if (!user?.id) return yield* Effect.fail(/* @__PURE__ */ new Error(`Could not resolve Twitter user id for @${username}`));
|
|
5895
|
-
return String(user.id);
|
|
5896
|
-
}
|
|
5897
|
-
const user = yield* lookupAuthenticatedUserEffect();
|
|
5898
|
-
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}`));
|
|
5899
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
|
|
5900
6030
|
});
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
});
|
|
5908
|
-
const query = new URLSearchParams({
|
|
5909
|
-
max_results: String(maxResults),
|
|
5910
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
5911
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics",
|
|
5912
|
-
"media.fields": MEDIA_FIELDS,
|
|
5913
|
-
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5914
|
-
});
|
|
5915
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5916
|
-
if (sinceId) query.set("since_id", sinceId);
|
|
5917
|
-
if (startTime) query.set("start_time", startTime);
|
|
5918
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
5919
|
-
args: [`/2/users/${resolvedUserId}/mentions?${query.toString()}`],
|
|
5920
|
-
username
|
|
5921
|
-
}));
|
|
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"
|
|
5922
6037
|
});
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
5936
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
5937
|
-
"media.fields": MEDIA_FIELDS,
|
|
5938
|
-
"user.fields": RICH_USER_FIELDS
|
|
5939
|
-
});
|
|
5940
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5941
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
5942
|
-
args: [`/2/users/${resolvedUserId}/timelines/reverse_chronological?${query.toString()}`],
|
|
5943
|
-
username,
|
|
5944
|
-
options: { timeoutMs }
|
|
5945
|
-
}));
|
|
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
|
|
5946
6050
|
});
|
|
5947
|
-
|
|
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
|
+
});
|
|
5948
6065
|
function toXurlMentionsResponse(payload) {
|
|
5949
6066
|
return {
|
|
5950
6067
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
@@ -5952,45 +6069,37 @@ function toXurlMentionsResponse(payload) {
|
|
|
5952
6069
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
5953
6070
|
};
|
|
5954
6071
|
}
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
userId
|
|
5960
|
-
});
|
|
5961
|
-
const requestMaxResults = capTimelineCollectionMaxResults(collection, maxResults, isPaginatedWalk);
|
|
5962
|
-
const query = new URLSearchParams({
|
|
5963
|
-
max_results: String(requestMaxResults),
|
|
5964
|
-
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
5965
|
-
"tweet.fields": "created_at,conversation_id,entities,public_metrics,referenced_tweets",
|
|
5966
|
-
"media.fields": MEDIA_FIELDS,
|
|
5967
|
-
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
5968
|
-
});
|
|
5969
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
5970
|
-
return toXurlMentionsResponse(yield* runOAuth2JsonCommandEffect({
|
|
5971
|
-
args: [`/2/users/${resolvedUserId}/${collection}?${query.toString()}`],
|
|
5972
|
-
username
|
|
5973
|
-
}));
|
|
6072
|
+
var listTimelineCollectionViaXurlEffect = Effect.fn("xurl.listTimelineCollection")(function* ({ collection, maxResults, username, userId, isPaginatedWalk = false, paginationToken }) {
|
|
6073
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6074
|
+
username,
|
|
6075
|
+
userId
|
|
5974
6076
|
});
|
|
5975
|
-
|
|
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
|
+
});
|
|
5976
6091
|
function listLikedTweetsViaXurlEffect(options) {
|
|
5977
6092
|
return listTimelineCollectionViaXurlEffect({
|
|
5978
6093
|
...options,
|
|
5979
6094
|
collection: "liked_tweets"
|
|
5980
6095
|
});
|
|
5981
6096
|
}
|
|
5982
|
-
function listLikedTweetsViaXurl(options) {
|
|
5983
|
-
return runEffectPromise(listLikedTweetsViaXurlEffect(options));
|
|
5984
|
-
}
|
|
5985
6097
|
function listBookmarkedTweetsViaXurlEffect(options) {
|
|
5986
6098
|
return listTimelineCollectionViaXurlEffect({
|
|
5987
6099
|
...options,
|
|
5988
6100
|
collection: "bookmarks"
|
|
5989
6101
|
});
|
|
5990
6102
|
}
|
|
5991
|
-
function listBookmarkedTweetsViaXurl(options) {
|
|
5992
|
-
return runEffectPromise(listBookmarkedTweetsViaXurlEffect(options));
|
|
5993
|
-
}
|
|
5994
6103
|
function listDirectMessageEventsViaXurlEffect({ maxResults, username, paginationToken }) {
|
|
5995
6104
|
const query = new URLSearchParams({
|
|
5996
6105
|
max_results: String(maxResults),
|
|
@@ -6009,31 +6118,26 @@ function listDirectMessageEventsViaXurlEffect({ maxResults, username, pagination
|
|
|
6009
6118
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6010
6119
|
})));
|
|
6011
6120
|
}
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
userId
|
|
6017
|
-
});
|
|
6018
|
-
const query = new URLSearchParams({
|
|
6019
|
-
max_results: String(maxResults),
|
|
6020
|
-
"user.fields": "id,username,name,description,verified,protected,public_metrics,profile_image_url,created_at"
|
|
6021
|
-
});
|
|
6022
|
-
if (paginationToken) query.set("pagination_token", paginationToken);
|
|
6023
|
-
const payload = yield* runOAuth2JsonCommandEffect({
|
|
6024
|
-
args: [`/2/users/${resolvedUserId}/${direction}?${query.toString()}`],
|
|
6025
|
-
username
|
|
6026
|
-
});
|
|
6027
|
-
return {
|
|
6028
|
-
data: Array.isArray(payload.data) ? payload.data : [],
|
|
6029
|
-
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6030
|
-
};
|
|
6121
|
+
var listFollowUsersViaXurlEffect = Effect.fn("xurl.listFollowUsers")(function* ({ direction, maxResults, username, userId, paginationToken }) {
|
|
6122
|
+
const resolvedUserId = yield* resolveUserIdEffect({
|
|
6123
|
+
username,
|
|
6124
|
+
userId
|
|
6031
6125
|
});
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
}
|
|
6036
|
-
|
|
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) => {
|
|
6037
6141
|
const query = new URLSearchParams({
|
|
6038
6142
|
max_results: "100",
|
|
6039
6143
|
"user.fields": "description,entities,location,public_metrics,profile_image_url,url,created_at,verified,verified_type"
|
|
@@ -6047,11 +6151,8 @@ function listBlockedUsersEffect(userId, paginationToken) {
|
|
|
6047
6151
|
nextToken: typeof meta?.next_token === "string" ? String(meta.next_token) : null
|
|
6048
6152
|
};
|
|
6049
6153
|
}));
|
|
6050
|
-
}
|
|
6051
|
-
|
|
6052
|
-
return runEffectPromise(listBlockedUsersEffect(userId, paginationToken));
|
|
6053
|
-
}
|
|
6054
|
-
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 }) => {
|
|
6055
6156
|
const query = new URLSearchParams({
|
|
6056
6157
|
max_results: String(maxResults),
|
|
6057
6158
|
expansions: MEDIA_EXPANSION,
|
|
@@ -6087,10 +6188,7 @@ function listUserTweetsEffect(userId, { maxResults, paginationToken, excludeRetw
|
|
|
6087
6188
|
...includes ? { includes } : {}
|
|
6088
6189
|
};
|
|
6089
6190
|
}));
|
|
6090
|
-
}
|
|
6091
|
-
function listUserTweets(userId, options) {
|
|
6092
|
-
return runEffectPromise(listUserTweetsEffect(userId, options));
|
|
6093
|
-
}
|
|
6191
|
+
});
|
|
6094
6192
|
function toXurlTweetsResponse(payload) {
|
|
6095
6193
|
return {
|
|
6096
6194
|
data: Array.isArray(payload.data) ? payload.data : [],
|
|
@@ -6098,7 +6196,17 @@ function toXurlTweetsResponse(payload) {
|
|
|
6098
6196
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6099
6197
|
};
|
|
6100
6198
|
}
|
|
6101
|
-
|
|
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 }) => {
|
|
6102
6210
|
const query = new URLSearchParams({
|
|
6103
6211
|
query: `conversation_id:${conversationId}`,
|
|
6104
6212
|
max_results: String(maxResults),
|
|
@@ -6123,8 +6231,8 @@ function searchRecentByConversationIdEffect(conversationId, { maxResults, pagina
|
|
|
6123
6231
|
signal,
|
|
6124
6232
|
onAttempt
|
|
6125
6233
|
})).pipe(Effect.map(toXurlTweetsResponse));
|
|
6126
|
-
}
|
|
6127
|
-
|
|
6234
|
+
});
|
|
6235
|
+
var searchRecentTweetsEffect = Effect.fn("xurl.searchRecentTweets")((searchQuery, { maxResults, paginationToken, startTime, endTime, username, timeoutMs }) => {
|
|
6128
6236
|
const query = new URLSearchParams({
|
|
6129
6237
|
query: searchQuery,
|
|
6130
6238
|
max_results: String(maxResults),
|
|
@@ -6142,8 +6250,8 @@ function searchRecentTweetsEffect(searchQuery, { maxResults, paginationToken, st
|
|
|
6142
6250
|
options: { timeoutMs },
|
|
6143
6251
|
useConfiguredCandidate: false
|
|
6144
6252
|
}).pipe(Effect.map(toXurlTweetsResponse));
|
|
6145
|
-
}
|
|
6146
|
-
|
|
6253
|
+
});
|
|
6254
|
+
var getTweetByIdEffect = Effect.fn("xurl.getTweetById")((id, { timeoutMs } = {}) => {
|
|
6147
6255
|
return runJsonCommandEffect([`/2/tweets/${id}?${new URLSearchParams({
|
|
6148
6256
|
expansions: AUTHOR_MEDIA_EXPANSIONS,
|
|
6149
6257
|
"tweet.fields": THREAD_TWEET_FIELDS,
|
|
@@ -6156,7 +6264,7 @@ function getTweetByIdEffect(id, { timeoutMs } = {}) {
|
|
|
6156
6264
|
meta: payload.meta && typeof payload.meta === "object" ? payload.meta : void 0
|
|
6157
6265
|
};
|
|
6158
6266
|
}));
|
|
6159
|
-
}
|
|
6267
|
+
});
|
|
6160
6268
|
function postViaXurlEffect(text) {
|
|
6161
6269
|
return runShortcutEffect(["post", text]);
|
|
6162
6270
|
}
|
|
@@ -6183,9 +6291,6 @@ function blockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6183
6291
|
JSON.stringify({ target_user_id: targetUserId })
|
|
6184
6292
|
]);
|
|
6185
6293
|
}
|
|
6186
|
-
function blockUserViaXurl(sourceUserId, targetUserId) {
|
|
6187
|
-
return runEffectPromise(blockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6188
|
-
}
|
|
6189
6294
|
function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6190
6295
|
return runMutationCommandEffect([
|
|
6191
6296
|
"-X",
|
|
@@ -6193,9 +6298,6 @@ function unblockUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6193
6298
|
`/2/users/${sourceUserId}/blocking/${targetUserId}`
|
|
6194
6299
|
]);
|
|
6195
6300
|
}
|
|
6196
|
-
function unblockUserViaXurl(sourceUserId, targetUserId) {
|
|
6197
|
-
return runEffectPromise(unblockUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6198
|
-
}
|
|
6199
6301
|
function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6200
6302
|
return runMutationCommandEffect([
|
|
6201
6303
|
"-X",
|
|
@@ -6205,9 +6307,6 @@ function muteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6205
6307
|
JSON.stringify({ target_user_id: targetUserId })
|
|
6206
6308
|
]);
|
|
6207
6309
|
}
|
|
6208
|
-
function muteUserViaXurl(sourceUserId, targetUserId) {
|
|
6209
|
-
return runEffectPromise(muteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6210
|
-
}
|
|
6211
6310
|
function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
6212
6311
|
return runMutationCommandEffect([
|
|
6213
6312
|
"-X",
|
|
@@ -6215,44 +6314,35 @@ function unmuteUserViaXurlEffect(sourceUserId, targetUserId) {
|
|
|
6215
6314
|
`/2/users/${sourceUserId}/muting/${targetUserId}`
|
|
6216
6315
|
]);
|
|
6217
6316
|
}
|
|
6218
|
-
function unmuteUserViaXurl(sourceUserId, targetUserId) {
|
|
6219
|
-
return runEffectPromise(unmuteUserViaXurlEffect(sourceUserId, targetUserId));
|
|
6220
|
-
}
|
|
6221
6317
|
//#endregion
|
|
6222
6318
|
//#region src/lib/live-transport-gateway.ts
|
|
6223
|
-
function fromPromise(run) {
|
|
6224
|
-
return Effect.tryPromise({
|
|
6225
|
-
try: run,
|
|
6226
|
-
catch: (error) => error instanceof Error ? error : new Error(String(error))
|
|
6227
|
-
});
|
|
6228
|
-
}
|
|
6229
6319
|
var liveTransportGateway = {
|
|
6230
6320
|
bird: {
|
|
6231
|
-
getAuthenticatedAccount: () => getAuthenticatedBirdAccountEffect(),
|
|
6232
|
-
listBookmarks: (
|
|
6233
|
-
listDirectMessages: (
|
|
6234
|
-
listFollowUsers: (
|
|
6235
|
-
listHomeTimeline: (
|
|
6236
|
-
listLikes: (
|
|
6237
|
-
listMentions: (
|
|
6238
|
-
listThread: (
|
|
6239
|
-
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)
|
|
6240
6330
|
},
|
|
6241
6331
|
xurl: {
|
|
6242
|
-
getTransportStatus: (...args) =>
|
|
6243
|
-
getTweetById: (
|
|
6244
|
-
listBookmarks: (...args) =>
|
|
6245
|
-
listDirectMessages: (
|
|
6246
|
-
listFollowUsers: (...args) =>
|
|
6247
|
-
listHomeTimeline: (
|
|
6248
|
-
listLikes: (...args) =>
|
|
6249
|
-
listMentions: (...args) =>
|
|
6250
|
-
listUserTweets: (...args) =>
|
|
6251
|
-
lookupAuthenticatedOAuth2User: (
|
|
6252
|
-
lookupAuthenticatedUser: (...args) =>
|
|
6253
|
-
lookupUsersByHandles: (...args) =>
|
|
6254
|
-
searchConversation: (
|
|
6255
|
-
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)
|
|
6256
6346
|
}
|
|
6257
6347
|
};
|
|
6258
6348
|
//#endregion
|
|
@@ -6414,8 +6504,8 @@ var MAX_AVATAR_BYTES = 2 * 1024 * 1024;
|
|
|
6414
6504
|
var MAX_AVATAR_DATA_URL_CHARS = MAX_AVATAR_BYTES * 4;
|
|
6415
6505
|
var REMOTE_AVATAR_TIMEOUT_MS = 1e4;
|
|
6416
6506
|
var DEFAULT_AVATAR_PREFETCH_CONCURRENCY = 8;
|
|
6417
|
-
var ALLOWED_REMOTE_AVATAR_HOSTS = new Set(["pbs.twimg.com"]);
|
|
6418
|
-
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([
|
|
6419
6509
|
"image/jpeg",
|
|
6420
6510
|
"image/png",
|
|
6421
6511
|
"image/webp",
|
|
@@ -8213,7 +8303,7 @@ function listMentionsByIds(db, accountId, tweetIds, limit) {
|
|
|
8213
8303
|
}
|
|
8214
8304
|
function mergeMentionThreadIntoLocalStore({ db, accountId, accountHandle, mentionIds, payload, source = "bird", writeThreadContextEdges = false }) {
|
|
8215
8305
|
const usersById = new Map((payload.includes?.users ?? []).map((user) => [user.id, user]));
|
|
8216
|
-
const groups = new Map([
|
|
8306
|
+
const groups = /* @__PURE__ */ new Map([
|
|
8217
8307
|
["mention", []],
|
|
8218
8308
|
["home", []],
|
|
8219
8309
|
["thread", []]
|
|
@@ -8274,7 +8364,7 @@ function fetchParentChainViaXurlEffect({ mention, maxDepth, timeoutMs, deadlineM
|
|
|
8274
8364
|
return Effect.gen(function* () {
|
|
8275
8365
|
const pages = [];
|
|
8276
8366
|
const warnings = [];
|
|
8277
|
-
const seenTweetIds = new Set([mention.id]);
|
|
8367
|
+
const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
|
|
8278
8368
|
let nextParentId = mention.replyToId;
|
|
8279
8369
|
let fallbackDepth = 0;
|
|
8280
8370
|
let generalReadTweets = 0;
|
|
@@ -8320,7 +8410,7 @@ function fetchParentChainViaXurlEffect({ mention, maxDepth, timeoutMs, deadlineM
|
|
|
8320
8410
|
}
|
|
8321
8411
|
function findMissingAncestorId(mention, payload) {
|
|
8322
8412
|
const tweetsById = new Map(payload.data.map((tweet) => [tweet.id, tweet]));
|
|
8323
|
-
const seenTweetIds = new Set([mention.id]);
|
|
8413
|
+
const seenTweetIds = /* @__PURE__ */ new Set([mention.id]);
|
|
8324
8414
|
const anchorTweet = tweetsById.get(mention.id) ?? mention.rawTweet;
|
|
8325
8415
|
let nextParentId = anchorTweet ? getReplyToId(anchorTweet) : mention.replyToId;
|
|
8326
8416
|
while (nextParentId) {
|
|
@@ -11433,7 +11523,7 @@ function runModeEffect(mode, options) {
|
|
|
11433
11523
|
}
|
|
11434
11524
|
function combineTweetSearchResults(left, right, limit) {
|
|
11435
11525
|
if (left.ok && right.ok) {
|
|
11436
|
-
const tweetIds = [
|
|
11526
|
+
const tweetIds = [.../* @__PURE__ */ new Set([...left.tweetIds, ...right.tweetIds])].slice(0, limit);
|
|
11437
11527
|
const liveSources = new Set([left.source, right.source].filter((source) => source !== "cache"));
|
|
11438
11528
|
return {
|
|
11439
11529
|
ok: true,
|
|
@@ -12236,7 +12326,7 @@ function resolveProfilesForHandlesEffect(handles, options = {}) {
|
|
|
12236
12326
|
unresolved = [];
|
|
12237
12327
|
}
|
|
12238
12328
|
if (unresolved.length > 0 && xurlFallback) {
|
|
12239
|
-
const xurlResult = yield*
|
|
12329
|
+
const xurlResult = yield* lookupUsersByHandlesEffect(unresolved).pipe(Effect.map((users) => ({
|
|
12240
12330
|
ok: true,
|
|
12241
12331
|
users
|
|
12242
12332
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -12335,6 +12425,8 @@ function toError$9(error) {
|
|
|
12335
12425
|
return error instanceof Error ? error : new Error(String(error));
|
|
12336
12426
|
}
|
|
12337
12427
|
function isXurlRateLimitError(error) {
|
|
12428
|
+
const tagged = error;
|
|
12429
|
+
if (tagged._tag === "XurlCommandError" && tagged.rateLimited === true) return true;
|
|
12338
12430
|
return error.message.includes("Too Many Requests") || error.message.includes("\"status\":429") || /\b429\b/.test(error.message);
|
|
12339
12431
|
}
|
|
12340
12432
|
function tryProfileSync(try_) {
|
|
@@ -13040,7 +13132,7 @@ var Route$11 = createFileRoute("/api/profile-analysis")({ server: { handlers: {
|
|
|
13040
13132
|
//#region src/lib/link-insights.ts
|
|
13041
13133
|
var DEFAULT_LIMIT$1 = 30;
|
|
13042
13134
|
var DEFAULT_COMMENTS_LIMIT = 8;
|
|
13043
|
-
var TRACKING_PARAMS = new Set([
|
|
13135
|
+
var TRACKING_PARAMS = /* @__PURE__ */ new Set([
|
|
13044
13136
|
"fbclid",
|
|
13045
13137
|
"gclid",
|
|
13046
13138
|
"igshid",
|
|
@@ -13059,17 +13151,17 @@ var VIDEO_HOST_SUFFIXES = [
|
|
|
13059
13151
|
"tiktok.com",
|
|
13060
13152
|
"loom.com"
|
|
13061
13153
|
];
|
|
13062
|
-
var VIDEO_EXACT_HOSTS = new Set([
|
|
13154
|
+
var VIDEO_EXACT_HOSTS = /* @__PURE__ */ new Set([
|
|
13063
13155
|
"youtu.be",
|
|
13064
13156
|
"clips.twitch.tv",
|
|
13065
13157
|
"vm.tiktok.com"
|
|
13066
13158
|
]);
|
|
13067
13159
|
var EXCLUDED_HOST_SUFFIXES = ["x.com", "twitter.com"];
|
|
13068
|
-
var EXCLUDED_EXACT_HOSTS = new Set(["t.co"]);
|
|
13160
|
+
var EXCLUDED_EXACT_HOSTS = /* @__PURE__ */ new Set(["t.co"]);
|
|
13069
13161
|
var URL_WHITESPACE = /\s+/g;
|
|
13070
13162
|
var RAW_URL_PATTERN = /https?:\/\/[^\s<>"'`]+/g;
|
|
13071
13163
|
var SQL_URL_EXPRESSION = "lower(coalesce(nullif(e.final_url, ''), nullif(e.expanded_url, ''), e.short_url))";
|
|
13072
|
-
var TITLE_SEGMENT_STOPWORDS = new Set([
|
|
13164
|
+
var TITLE_SEGMENT_STOPWORDS = /* @__PURE__ */ new Set([
|
|
13073
13165
|
"a",
|
|
13074
13166
|
"about",
|
|
13075
13167
|
"blog",
|
|
@@ -13082,7 +13174,7 @@ var TITLE_SEGMENT_STOPWORDS = new Set([
|
|
|
13082
13174
|
"story",
|
|
13083
13175
|
"watch"
|
|
13084
13176
|
]);
|
|
13085
|
-
var TITLE_WORD_OVERRIDES = new Map([
|
|
13177
|
+
var TITLE_WORD_OVERRIDES = /* @__PURE__ */ new Map([
|
|
13086
13178
|
["ai", "AI"],
|
|
13087
13179
|
["api", "API"],
|
|
13088
13180
|
["codex", "Codex"],
|
|
@@ -13093,7 +13185,7 @@ var TITLE_WORD_OVERRIDES = new Map([
|
|
|
13093
13185
|
["url", "URL"],
|
|
13094
13186
|
["wwdc", "WWDC"]
|
|
13095
13187
|
]);
|
|
13096
|
-
var TITLE_SMALL_WORDS = new Set([
|
|
13188
|
+
var TITLE_SMALL_WORDS = /* @__PURE__ */ new Set([
|
|
13097
13189
|
"a",
|
|
13098
13190
|
"an",
|
|
13099
13191
|
"and",
|
|
@@ -13242,7 +13334,7 @@ function stripUrls(text, urls) {
|
|
|
13242
13334
|
let output = text;
|
|
13243
13335
|
for (const url of urls) {
|
|
13244
13336
|
if (!url) continue;
|
|
13245
|
-
const variants = new Set([url]);
|
|
13337
|
+
const variants = /* @__PURE__ */ new Set([url]);
|
|
13246
13338
|
try {
|
|
13247
13339
|
const parsed = new URL(url);
|
|
13248
13340
|
variants.add(parsed.href);
|
|
@@ -13629,9 +13721,9 @@ function getLinkInsights(query = {}) {
|
|
|
13629
13721
|
kind: rowKind,
|
|
13630
13722
|
lastSeenAt: row.created_at,
|
|
13631
13723
|
mentions: [mention],
|
|
13632
|
-
seenMentions: new Set([mention.id]),
|
|
13633
|
-
sharers: new Set([sharerKey]),
|
|
13634
|
-
sharerProfiles: mention.sharedBy ? new Map([[mention.sharedBy.id, mention.sharedBy]]) : /* @__PURE__ */ new Map(),
|
|
13724
|
+
seenMentions: /* @__PURE__ */ new Set([mention.id]),
|
|
13725
|
+
sharers: /* @__PURE__ */ new Set([sharerKey]),
|
|
13726
|
+
sharerProfiles: mention.sharedBy ? /* @__PURE__ */ new Map([[mention.sharedBy.id, mention.sharedBy]]) : /* @__PURE__ */ new Map(),
|
|
13635
13727
|
shareCount: 1,
|
|
13636
13728
|
title: row.title,
|
|
13637
13729
|
topSharer: mention.sharedBy ?? null,
|
|
@@ -14497,7 +14589,7 @@ var LETTER_PATTERN = /[a-z]/i;
|
|
|
14497
14589
|
var URL_OR_HANDLE_PATTERN = /(https?:\/\/|@|[A-Z0-9._%+-]+@[A-Z0-9.-]+)/i;
|
|
14498
14590
|
var MONEY_PATTERN = /\$\s?\d/;
|
|
14499
14591
|
var EMOJI_BLOCK_PATTERN = /[\u{1f1e6}-\u{1f1ff}\u{1f300}-\u{1faff}\u{2600}-\u{27bf}]/gu;
|
|
14500
|
-
var GENERIC_LOCATIONS = new Set([
|
|
14592
|
+
var GENERIC_LOCATIONS = /* @__PURE__ */ new Set([
|
|
14501
14593
|
"earth",
|
|
14502
14594
|
"everywhere",
|
|
14503
14595
|
"internet",
|
|
@@ -14515,7 +14607,7 @@ var GENERIC_LOCATIONS = new Set([
|
|
|
14515
14607
|
"world",
|
|
14516
14608
|
"world wide web"
|
|
14517
14609
|
]);
|
|
14518
|
-
var DROP_WORDS = new Set([
|
|
14610
|
+
var DROP_WORDS = /* @__PURE__ */ new Set([
|
|
14519
14611
|
"a",
|
|
14520
14612
|
"an",
|
|
14521
14613
|
"and",
|
|
@@ -15069,7 +15161,7 @@ function upsertUrlExpansion(db, item) {
|
|
|
15069
15161
|
var FETCH_TIMEOUT_MS = 12e3;
|
|
15070
15162
|
var MAX_HTML_CHARS = 2e6;
|
|
15071
15163
|
var MAX_REDIRECTS = 4;
|
|
15072
|
-
var NO_BODY_STATUS_CODES = new Set([
|
|
15164
|
+
var NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
15073
15165
|
204,
|
|
15074
15166
|
205,
|
|
15075
15167
|
304
|
|
@@ -15139,15 +15231,6 @@ function isInjectedFetchAllowed() {
|
|
|
15139
15231
|
function stripAddressBrackets(value) {
|
|
15140
15232
|
return value.replace(/^\[|\]$/g, "");
|
|
15141
15233
|
}
|
|
15142
|
-
function withTimeout(promise, ms) {
|
|
15143
|
-
let timer = null;
|
|
15144
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
15145
|
-
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Link preview request timed out")), ms);
|
|
15146
|
-
});
|
|
15147
|
-
return Promise.race([promise, timeout]).finally(() => {
|
|
15148
|
-
if (timer) clearTimeout(timer);
|
|
15149
|
-
});
|
|
15150
|
-
}
|
|
15151
15234
|
async function resolvePublicAddresses(hostname) {
|
|
15152
15235
|
const normalized = stripAddressBrackets(hostname);
|
|
15153
15236
|
if (net.isIP(normalized)) return [normalized];
|
|
@@ -15160,19 +15243,24 @@ function validateResolvedAddresses(addresses) {
|
|
|
15160
15243
|
if (addresses.length === 0) throw new Error("Link preview host did not resolve");
|
|
15161
15244
|
if (addresses.some(isBlockedAddress)) throw new Error("Link preview URL resolves to a private address");
|
|
15162
15245
|
}
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
family
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15246
|
+
var resolveSafeAddressesEffect = Effect.fn("linkPreview.resolveSafeAddresses")((hostname, resolveHost, timeoutMs) => tryPromise(() => resolveHost(stripAddressBrackets(hostname))).pipe(Effect.timeoutFail({
|
|
15247
|
+
duration: timeoutMs,
|
|
15248
|
+
onTimeout: () => /* @__PURE__ */ new Error("Link preview request timed out")
|
|
15249
|
+
}), Effect.flatMap((addresses) => Effect.try({
|
|
15250
|
+
try: () => {
|
|
15251
|
+
validateResolvedAddresses(addresses);
|
|
15252
|
+
return addresses.map((address) => {
|
|
15253
|
+
const normalized = stripAddressBrackets(address);
|
|
15254
|
+
const family = net.isIP(normalized);
|
|
15255
|
+
if (family !== 4 && family !== 6) throw new Error("Link preview host resolved to an invalid address");
|
|
15256
|
+
return {
|
|
15257
|
+
address: normalized,
|
|
15258
|
+
family
|
|
15259
|
+
};
|
|
15260
|
+
});
|
|
15261
|
+
},
|
|
15262
|
+
catch: (error) => error
|
|
15263
|
+
}))));
|
|
15176
15264
|
function headersFromIncoming(headers) {
|
|
15177
15265
|
const result = new Headers();
|
|
15178
15266
|
for (const [key, value] of Object.entries(headers)) if (Array.isArray(value)) for (const item of value) result.append(key, item);
|
|
@@ -15268,7 +15356,7 @@ function nodeSafeFetch(url, options) {
|
|
|
15268
15356
|
return fetchAddress(address, Math.max(1, Math.ceil(remainingMs / remainingAddresses)));
|
|
15269
15357
|
}), Promise.reject(/* @__PURE__ */ new Error("Link preview host did not resolve")));
|
|
15270
15358
|
}
|
|
15271
|
-
|
|
15359
|
+
var safePreviewFetchEffect = Effect.fn("linkPreview.safePreviewFetch")(function* (url, options) {
|
|
15272
15360
|
const resolveHost = options.resolveHost ?? (options.fetchImpl ? null : (hostname) => resolvePublicAddresses(hostname));
|
|
15273
15361
|
const timeoutMs = options.timeoutMs ?? FETCH_TIMEOUT_MS;
|
|
15274
15362
|
const method = options.method ?? "GET";
|
|
@@ -15279,43 +15367,39 @@ function safePreviewFetchEffect(url, options) {
|
|
|
15279
15367
|
accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
|
|
15280
15368
|
"accept-language": "en-US,en;q=0.9"
|
|
15281
15369
|
};
|
|
15282
|
-
return Effect.
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
15308
|
-
|
|
15309
|
-
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
}
|
|
15316
|
-
return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15317
|
-
});
|
|
15318
|
-
}
|
|
15370
|
+
if (options.fetchImpl && !isInjectedFetchAllowed()) return yield* Effect.fail(/* @__PURE__ */ new Error("Custom link preview fetch is only available in tests"));
|
|
15371
|
+
let currentUrl = url;
|
|
15372
|
+
for (let redirect = 0; redirect <= MAX_REDIRECTS; redirect += 1) {
|
|
15373
|
+
if (Date.now() >= deadline) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview request timed out"));
|
|
15374
|
+
const parsed = yield* Effect.try({
|
|
15375
|
+
try: () => assertSafePreviewUrl(currentUrl),
|
|
15376
|
+
catch: (error) => error
|
|
15377
|
+
});
|
|
15378
|
+
if (options.fetchImpl && resolveHost) yield* resolveSafeAddressesEffect(parsed.hostname, resolveHost, remainingTimeoutMs());
|
|
15379
|
+
if (Date.now() >= deadline) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview request timed out"));
|
|
15380
|
+
const response = options.fetchImpl ? yield* tryPromise(() => options.fetchImpl?.(parsed.toString(), {
|
|
15381
|
+
headers,
|
|
15382
|
+
method,
|
|
15383
|
+
redirect: "manual",
|
|
15384
|
+
signal: AbortSignal.timeout(remainingTimeoutMs())
|
|
15385
|
+
})) : 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, {
|
|
15386
|
+
addresses,
|
|
15387
|
+
headers,
|
|
15388
|
+
method,
|
|
15389
|
+
timeoutMs: remainingTimeoutMs()
|
|
15390
|
+
}))));
|
|
15391
|
+
if (response.status < 300 || response.status >= 400) return response;
|
|
15392
|
+
const location = response.headers.get("location");
|
|
15393
|
+
if (!location) return response;
|
|
15394
|
+
yield* cancelResponseBodyEffect(response);
|
|
15395
|
+
if (redirect === MAX_REDIRECTS) return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15396
|
+
currentUrl = yield* Effect.try({
|
|
15397
|
+
try: () => new URL(location, parsed).toString(),
|
|
15398
|
+
catch: (error) => error
|
|
15399
|
+
});
|
|
15400
|
+
}
|
|
15401
|
+
return yield* Effect.fail(/* @__PURE__ */ new Error("Link preview redirected too many times"));
|
|
15402
|
+
});
|
|
15319
15403
|
function readResponseTextEffect(response) {
|
|
15320
15404
|
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
15321
15405
|
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"))));
|
|
@@ -15379,39 +15463,37 @@ function extractLinkPreviewMetadata(html, url) {
|
|
|
15379
15463
|
siteName
|
|
15380
15464
|
};
|
|
15381
15465
|
}
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15403
|
-
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
})));
|
|
15414
|
-
}
|
|
15466
|
+
var fetchLinkPreviewMetadataEffect = Effect.fn("linkPreview.fetchMetadata")(function* (url, _options = {}) {
|
|
15467
|
+
const response = yield* safePreviewFetchEffect(url, _options);
|
|
15468
|
+
const finalUrl = response.url || url;
|
|
15469
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
15470
|
+
if (!response.ok) {
|
|
15471
|
+
yield* cancelResponseBodyEffect(response);
|
|
15472
|
+
return yield* Effect.fail(/* @__PURE__ */ new Error(`HTTP ${response.status}`));
|
|
15473
|
+
}
|
|
15474
|
+
if (contentType.toLowerCase().startsWith("image/")) {
|
|
15475
|
+
yield* cancelResponseBodyEffect(response);
|
|
15476
|
+
return {
|
|
15477
|
+
url: finalUrl,
|
|
15478
|
+
title: hostLabel(finalUrl),
|
|
15479
|
+
description: null,
|
|
15480
|
+
imageUrl: finalUrl,
|
|
15481
|
+
siteName: hostLabel(finalUrl)
|
|
15482
|
+
};
|
|
15483
|
+
}
|
|
15484
|
+
const content = yield* readResponseTextEffect(response);
|
|
15485
|
+
return yield* Effect.try({
|
|
15486
|
+
try: () => extractLinkPreviewMetadata(content.slice(0, MAX_HTML_CHARS), finalUrl),
|
|
15487
|
+
catch: (error) => error
|
|
15488
|
+
});
|
|
15489
|
+
}, (effect, url) => Effect.catchAll(effect, (error) => Effect.succeed({
|
|
15490
|
+
url,
|
|
15491
|
+
title: hostLabel(url),
|
|
15492
|
+
description: null,
|
|
15493
|
+
imageUrl: youtubeThumbnail(url),
|
|
15494
|
+
siteName: hostLabel(url),
|
|
15495
|
+
error: error instanceof Error ? error.message : String(error)
|
|
15496
|
+
})));
|
|
15415
15497
|
function readCachedPreview(db, url, shortUrl) {
|
|
15416
15498
|
return db.prepare(`
|
|
15417
15499
|
select short_url, expanded_url, final_url, status, title, description,
|
|
@@ -15465,16 +15547,14 @@ function persistPreview(db, url, shortUrl, cached, preview) {
|
|
|
15465
15547
|
updatedAt: now
|
|
15466
15548
|
}));
|
|
15467
15549
|
}
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
|
|
15476
|
-
});
|
|
15477
|
-
}
|
|
15550
|
+
var getOrFetchLinkPreviewEffect = Effect.fn("linkPreview.getOrFetch")(function* (url, options = {}) {
|
|
15551
|
+
const db = getNativeDb({ seedDemoData: false });
|
|
15552
|
+
const cached = readCachedPreview(db, url, options.shortUrl);
|
|
15553
|
+
if (cached && hasUsefulPreview(cached) && !options.refresh) return rowToPreview(cached);
|
|
15554
|
+
const preview = yield* fetchLinkPreviewMetadataEffect(cached?.final_url || cached?.expanded_url || url, options);
|
|
15555
|
+
persistPreview(db, url, options.shortUrl, cached, preview);
|
|
15556
|
+
return preview;
|
|
15557
|
+
});
|
|
15478
15558
|
({ ...__test__$1 });
|
|
15479
15559
|
//#endregion
|
|
15480
15560
|
//#region src/routes/api/link-preview.tsx
|
|
@@ -16073,9 +16153,6 @@ function readBirdStatusViaBirdEffect(query) {
|
|
|
16073
16153
|
]);
|
|
16074
16154
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
16075
16155
|
}
|
|
16076
|
-
function readBirdStatusViaBird(query) {
|
|
16077
|
-
return runEffectPromise(readBirdStatusViaBirdEffect(query));
|
|
16078
|
-
}
|
|
16079
16156
|
function toBirdLookupUser(payload) {
|
|
16080
16157
|
const user = payload.user && typeof payload.user === "object" ? payload.user : null;
|
|
16081
16158
|
if (!user) throw new Error("bird user payload missing user object");
|
|
@@ -16121,9 +16198,6 @@ function lookupProfileViaBirdEffect(query) {
|
|
|
16121
16198
|
});
|
|
16122
16199
|
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
16123
16200
|
}
|
|
16124
|
-
function lookupProfileViaBird(query) {
|
|
16125
|
-
return runEffectPromise(lookupProfileViaBirdEffect(query));
|
|
16126
|
-
}
|
|
16127
16201
|
function runVerifiedBirdMutationEffect({ action, query, verifyField, expectedValue }) {
|
|
16128
16202
|
return Effect.gen(function* () {
|
|
16129
16203
|
const safeQuery = yield* Effect.try({
|
|
@@ -16172,9 +16246,6 @@ function blockUserViaBirdEffect(query) {
|
|
|
16172
16246
|
expectedValue: true
|
|
16173
16247
|
});
|
|
16174
16248
|
}
|
|
16175
|
-
function blockUserViaBird(query) {
|
|
16176
|
-
return runEffectPromise(blockUserViaBirdEffect(query));
|
|
16177
|
-
}
|
|
16178
16249
|
function unblockUserViaBirdEffect(query) {
|
|
16179
16250
|
return runVerifiedBirdMutationEffect({
|
|
16180
16251
|
action: "unblock",
|
|
@@ -16183,9 +16254,6 @@ function unblockUserViaBirdEffect(query) {
|
|
|
16183
16254
|
expectedValue: false
|
|
16184
16255
|
});
|
|
16185
16256
|
}
|
|
16186
|
-
function unblockUserViaBird(query) {
|
|
16187
|
-
return runEffectPromise(unblockUserViaBirdEffect(query));
|
|
16188
|
-
}
|
|
16189
16257
|
function muteUserViaBirdEffect(query) {
|
|
16190
16258
|
return runVerifiedBirdMutationEffect({
|
|
16191
16259
|
action: "mute",
|
|
@@ -16194,9 +16262,6 @@ function muteUserViaBirdEffect(query) {
|
|
|
16194
16262
|
expectedValue: true
|
|
16195
16263
|
});
|
|
16196
16264
|
}
|
|
16197
|
-
function muteUserViaBird(query) {
|
|
16198
|
-
return runEffectPromise(muteUserViaBirdEffect(query));
|
|
16199
|
-
}
|
|
16200
16265
|
function unmuteUserViaBirdEffect(query) {
|
|
16201
16266
|
return runVerifiedBirdMutationEffect({
|
|
16202
16267
|
action: "unmute",
|
|
@@ -16205,9 +16270,6 @@ function unmuteUserViaBirdEffect(query) {
|
|
|
16205
16270
|
expectedValue: false
|
|
16206
16271
|
});
|
|
16207
16272
|
}
|
|
16208
|
-
function unmuteUserViaBird(query) {
|
|
16209
|
-
return runEffectPromise(unmuteUserViaBirdEffect(query));
|
|
16210
|
-
}
|
|
16211
16273
|
//#endregion
|
|
16212
16274
|
//#region src/lib/moderation-target.ts
|
|
16213
16275
|
function toError$4(error) {
|
|
@@ -16263,7 +16325,7 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
16263
16325
|
if (local && !local.profile.id.startsWith("profile_group_") && local.externalUserId) return local;
|
|
16264
16326
|
let user;
|
|
16265
16327
|
let lastError;
|
|
16266
|
-
const birdResult = yield*
|
|
16328
|
+
const birdResult = yield* lookupProfileViaBirdEffect(local?.profile.handle ?? normalizedQuery).pipe(Effect.map((value) => ({
|
|
16267
16329
|
ok: true,
|
|
16268
16330
|
value
|
|
16269
16331
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -16273,7 +16335,7 @@ function resolveProfileEffect(query, providedDb) {
|
|
|
16273
16335
|
if (birdResult.ok) user = birdResult.value ?? void 0;
|
|
16274
16336
|
else lastError = birdResult.error;
|
|
16275
16337
|
if (!user) {
|
|
16276
|
-
const xurlResult = yield*
|
|
16338
|
+
const xurlResult = yield* (/^\d+$/.test(normalizedQuery) ? lookupUsersByIdsEffect([normalizedQuery]) : lookupUsersByHandlesEffect([local?.profile.handle ?? normalizedQuery])).pipe(Effect.map((value) => ({
|
|
16277
16339
|
ok: true,
|
|
16278
16340
|
value
|
|
16279
16341
|
})), Effect.catchAll((error) => Effect.succeed({
|
|
@@ -16320,7 +16382,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
16320
16382
|
return Effect.gen(function* () {
|
|
16321
16383
|
if (!expectedAccount) return null;
|
|
16322
16384
|
if (liveWritesDisabled$1()) return null;
|
|
16323
|
-
const sourceUser = yield*
|
|
16385
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
16324
16386
|
const sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
16325
16387
|
const sourceUsername = sourceUser && typeof sourceUser.username === "string" ? profileHandleKey(sourceUser.username) : "";
|
|
16326
16388
|
const expectedExternalUserId = expectedAccount.externalUserId?.trim() ?? "";
|
|
@@ -16336,7 +16398,7 @@ function verifyExpectedAccountEffect(expectedAccount) {
|
|
|
16336
16398
|
function runBirdActionEffect(action, query) {
|
|
16337
16399
|
return Effect.gen(function* () {
|
|
16338
16400
|
return {
|
|
16339
|
-
...yield*
|
|
16401
|
+
...yield* action === "block" ? blockUserViaBirdEffect(query) : action === "unblock" ? unblockUserViaBirdEffect(query) : action === "mute" ? muteUserViaBirdEffect(query) : unmuteUserViaBirdEffect(query),
|
|
16340
16402
|
transport: "bird"
|
|
16341
16403
|
};
|
|
16342
16404
|
});
|
|
@@ -16359,7 +16421,7 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
16359
16421
|
};
|
|
16360
16422
|
let sourceUserId = verifiedSourceUserId ?? "";
|
|
16361
16423
|
if (!sourceUserId) {
|
|
16362
|
-
const sourceUser = yield*
|
|
16424
|
+
const sourceUser = yield* lookupAuthenticatedUserFreshEffect();
|
|
16363
16425
|
sourceUserId = sourceUser && typeof sourceUser.id === "string" ? sourceUser.id : "";
|
|
16364
16426
|
}
|
|
16365
16427
|
if (!sourceUserId) return {
|
|
@@ -16367,12 +16429,12 @@ function runXurlActionEffect(action, query, targetUserId, verifiedSourceUserId)
|
|
|
16367
16429
|
output: "xurl authenticated user unavailable",
|
|
16368
16430
|
transport: "xurl"
|
|
16369
16431
|
};
|
|
16370
|
-
const result = yield*
|
|
16432
|
+
const result = yield* action === "block" ? blockUserViaXurlEffect(sourceUserId, targetUserId) : action === "unblock" ? unblockUserViaXurlEffect(sourceUserId, targetUserId) : action === "mute" ? muteUserViaXurlEffect(sourceUserId, targetUserId) : unmuteUserViaXurlEffect(sourceUserId, targetUserId);
|
|
16371
16433
|
if (!result.ok) return {
|
|
16372
16434
|
...result,
|
|
16373
16435
|
transport: "xurl"
|
|
16374
16436
|
};
|
|
16375
|
-
const status = yield*
|
|
16437
|
+
const status = yield* readBirdStatusViaBirdEffect(query);
|
|
16376
16438
|
const { field: verifyField, expected: expectedValue } = getVerifyExpectation(action);
|
|
16377
16439
|
const actualValue = status && typeof status[verifyField] === "boolean" ? Boolean(status[verifyField]) : null;
|
|
16378
16440
|
if (actualValue === null) return {
|
|
@@ -16776,7 +16838,7 @@ function syncBlocksEffect(accountId) {
|
|
|
16776
16838
|
}
|
|
16777
16839
|
};
|
|
16778
16840
|
return yield* Effect.gen(function* () {
|
|
16779
|
-
const me = yield*
|
|
16841
|
+
const me = yield* lookupAuthenticatedUserFreshEffect();
|
|
16780
16842
|
const sourceUserId = typeof me?.id === "string" && me.id.length > 0 ? me.id : null;
|
|
16781
16843
|
const sourceUsername = typeof me?.username === "string" ? me.username.replace(/^@/, "") : "";
|
|
16782
16844
|
const accountExternalUserId = account?.external_user_id?.trim() ?? "";
|
|
@@ -16814,7 +16876,7 @@ function syncBlocksEffect(accountId) {
|
|
|
16814
16876
|
let pageCount = 0;
|
|
16815
16877
|
let completed = false;
|
|
16816
16878
|
do {
|
|
16817
|
-
const page = yield*
|
|
16879
|
+
const page = yield* listBlockedUsersEffect(sourceUserId, nextToken ?? void 0);
|
|
16818
16880
|
const pageProfileIds = yield* databaseWriteEffect((writeDb) => {
|
|
16819
16881
|
return page.items.map((user) => {
|
|
16820
16882
|
const resolved = upsertProfileFromXUser(writeDb, user);
|