birdclaw 0.9.2 → 0.9.4

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