birdclaw 0.9.1 → 0.9.3

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