birdclaw 0.8.1 → 0.8.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 (103) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +2 -4
  3. package/scripts/browser-perf.mjs +27 -0
  4. package/src/cli/command-context.ts +17 -0
  5. package/src/cli/register-analysis.ts +500 -0
  6. package/src/cli/register-compose.ts +40 -0
  7. package/src/cli/register-graph.ts +132 -0
  8. package/src/cli/register-inbox.ts +41 -0
  9. package/src/cli/register-storage.ts +106 -0
  10. package/src/cli.ts +30 -750
  11. package/src/components/AccountSwitcher.tsx +7 -15
  12. package/src/components/AvatarChip.tsx +1 -1
  13. package/src/components/AvatarPreload.ts +149 -0
  14. package/src/components/ConversationThread.tsx +4 -0
  15. package/src/components/EmbeddedTweetCard.tsx +4 -0
  16. package/src/components/FloatingPreview.tsx +382 -0
  17. package/src/components/MarkdownCitations.tsx +680 -0
  18. package/src/components/MarkdownViewer.tsx +7 -715
  19. package/src/components/ProfileAnalysisClient.ts +191 -0
  20. package/src/components/ProfileAnalysisStream.tsx +16 -185
  21. package/src/components/ProfilePreview.tsx +41 -81
  22. package/src/components/TimelineCard.tsx +18 -2
  23. package/src/components/TweetArticleCard.tsx +66 -0
  24. package/src/components/TweetRichText.tsx +33 -2
  25. package/src/components/links-controller.ts +162 -0
  26. package/src/components/links-model.ts +198 -0
  27. package/src/components/network-map-controller.ts +84 -0
  28. package/src/components/network-map-model.ts +255 -0
  29. package/src/components/useDebouncedValue.ts +12 -0
  30. package/src/components/useTimelineRouteData.ts +142 -170
  31. package/src/lib/analysis-runtime.ts +238 -0
  32. package/src/lib/api-client.ts +16 -100
  33. package/src/lib/api-contracts.ts +328 -0
  34. package/src/lib/archive-finder.ts +38 -0
  35. package/src/lib/archive-import-plan.ts +102 -0
  36. package/src/lib/archive-import.ts +170 -239
  37. package/src/lib/authored-live.ts +77 -182
  38. package/src/lib/backup.ts +335 -424
  39. package/src/lib/bird.ts +32 -1
  40. package/src/lib/blocks-write.ts +30 -26
  41. package/src/lib/blocks.ts +18 -20
  42. package/src/lib/database-metrics.ts +88 -0
  43. package/src/lib/database-migrations.ts +34 -0
  44. package/src/lib/database-schema.ts +312 -0
  45. package/src/lib/database-writer.ts +69 -0
  46. package/src/lib/db.ts +141 -334
  47. package/src/lib/dm-read-model.ts +533 -0
  48. package/src/lib/dms-live.ts +34 -97
  49. package/src/lib/follow-graph.ts +17 -27
  50. package/src/lib/import-repository.ts +138 -0
  51. package/src/lib/inbox.ts +2 -1
  52. package/src/lib/live-sync-engine.ts +209 -0
  53. package/src/lib/live-transport-gateway.ts +128 -0
  54. package/src/lib/mention-threads-live.ts +90 -176
  55. package/src/lib/mentions-export.ts +1 -1
  56. package/src/lib/mentions-live.ts +57 -225
  57. package/src/lib/moderation-target.ts +15 -4
  58. package/src/lib/moderation-write.ts +1 -1
  59. package/src/lib/mutes-write.ts +30 -26
  60. package/src/lib/openai-response-runtime.ts +251 -0
  61. package/src/lib/paginated-sync.ts +93 -0
  62. package/src/lib/period-digest.ts +116 -304
  63. package/src/lib/profile-analysis.ts +37 -111
  64. package/src/lib/queries.ts +6 -2380
  65. package/src/lib/query-actions.ts +437 -0
  66. package/src/lib/query-client.tsx +47 -0
  67. package/src/lib/query-read-model-shared.ts +52 -0
  68. package/src/lib/query-read-models.ts +5 -0
  69. package/src/lib/query-resource.ts +41 -0
  70. package/src/lib/query-status.ts +164 -0
  71. package/src/lib/research.ts +1 -1
  72. package/src/lib/runtime-services.ts +20 -0
  73. package/src/lib/search-discussion.ts +75 -279
  74. package/src/lib/server-runtime-services.ts +30 -0
  75. package/src/lib/sqlite.ts +53 -14
  76. package/src/lib/streaming-ingestion.ts +240 -0
  77. package/src/lib/sync-cache.ts +6 -1
  78. package/src/lib/sync-plan.ts +175 -0
  79. package/src/lib/timeline-collections-live.ts +83 -256
  80. package/src/lib/timeline-live.ts +86 -235
  81. package/src/lib/timeline-read-model.ts +1191 -0
  82. package/src/lib/tweet-render.ts +78 -0
  83. package/src/lib/tweet-repository.ts +156 -0
  84. package/src/lib/tweet-search-live.ts +63 -166
  85. package/src/lib/types.ts +8 -0
  86. package/src/lib/ui.ts +1 -1
  87. package/src/lib/web-sync.ts +67 -50
  88. package/src/lib/whois.ts +2 -1
  89. package/src/router.tsx +1 -1
  90. package/src/routes/__root.tsx +11 -21
  91. package/src/routes/api/action.tsx +1 -1
  92. package/src/routes/api/conversation.tsx +1 -1
  93. package/src/routes/api/query.tsx +32 -26
  94. package/src/routes/api/status.tsx +6 -2
  95. package/src/routes/api/sync.tsx +5 -2
  96. package/src/routes/blocks.tsx +97 -131
  97. package/src/routes/data-sources.tsx +17 -25
  98. package/src/routes/dms.tsx +168 -167
  99. package/src/routes/inbox.tsx +63 -57
  100. package/src/routes/links.tsx +31 -329
  101. package/src/routes/network-map.tsx +41 -344
  102. package/src/routes/rate-limits.tsx +17 -21
  103. package/vite.config.ts +0 -2
@@ -1,11 +1,14 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { Effect } from "effect";
3
3
  import { maybeAutoSyncBackupEffect } from "./backup";
4
- import { getBirdclawPaths } from "./config";
5
4
  import { syncDirectMessagesViaCachedBirdEffect } from "./dms-live";
6
5
  import { runEffectBackground, runEffectPromise } from "./effect-runtime";
7
6
  import { syncMentionThreadsEffect } from "./mention-threads-live";
8
7
  import { syncMentionsEffect } from "./mentions-live";
8
+ import {
9
+ defaultServerRuntimeServices,
10
+ type ServerRuntimeServices,
11
+ } from "./server-runtime-services";
9
12
  import NativeSqliteDatabase from "./sqlite";
10
13
  import { syncTimelineCollectionEffect } from "./timeline-collections-live";
11
14
  import { syncHomeTimelineEffect } from "./timeline-live";
@@ -69,6 +72,7 @@ interface WebSyncPlan {
69
72
  run: (
70
73
  accountId: string | undefined,
71
74
  options: WebSyncOptions,
75
+ runtime: ServerRuntimeServices,
72
76
  ) => Effect.Effect<WebSyncStep[], unknown>;
73
77
  }
74
78
 
@@ -132,12 +136,12 @@ const WEB_SYNC_PLANS: Record<WebSyncKind, WebSyncPlan> = {
132
136
  timeline: {
133
137
  label: "Home timeline",
134
138
  accountAware: true,
135
- run: (account) =>
139
+ run: (account, _options, runtime) =>
136
140
  Effect.gen(function* () {
137
141
  const result = yield* syncHomeTimelineEffect({
138
142
  account,
139
143
  mode:
140
- !account || account === resolveDefaultSyncAccountId()
144
+ !account || account === resolveDefaultSyncAccountId(runtime)
141
145
  ? "auto"
142
146
  : "xurl",
143
147
  limit: 100,
@@ -201,12 +205,14 @@ const WEB_SYNC_PLANS: Record<WebSyncKind, WebSyncPlan> = {
201
205
  likes: {
202
206
  label: "Likes",
203
207
  accountAware: true,
204
- run: (account) => syncSavedCollection("likes", account),
208
+ run: (account, _options, runtime) =>
209
+ syncSavedCollection("likes", account, runtime),
205
210
  },
206
211
  bookmarks: {
207
212
  label: "Bookmarks",
208
213
  accountAware: true,
209
- run: (account) => syncSavedCollection("bookmarks", account),
214
+ run: (account, _options, runtime) =>
215
+ syncSavedCollection("bookmarks", account, runtime),
210
216
  },
211
217
  dms: {
212
218
  label: "Direct messages",
@@ -242,10 +248,11 @@ const WEB_SYNC_PLANS: Record<WebSyncKind, WebSyncPlan> = {
242
248
  function syncSavedCollection(
243
249
  kind: "likes" | "bookmarks",
244
250
  account: string | undefined,
251
+ runtime: ServerRuntimeServices,
245
252
  ): Effect.Effect<WebSyncStep[], unknown> {
246
253
  return Effect.gen(function* () {
247
254
  const isNonDefaultAccount =
248
- account !== undefined && account !== resolveDefaultSyncAccountId();
255
+ account !== undefined && account !== resolveDefaultSyncAccountId(runtime);
249
256
  const result = yield* syncTimelineCollectionEffect({
250
257
  kind,
251
258
  account,
@@ -270,13 +277,14 @@ export function performWebSyncEffect(
270
277
  kind: WebSyncKind,
271
278
  accountId?: string,
272
279
  options: WebSyncOptions = {},
280
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
273
281
  ) {
274
282
  return Effect.gen(function* () {
275
- const startedAt = new Date().toISOString();
276
- const steps = yield* WEB_SYNC_PLANS[kind].run(accountId, options);
283
+ const startedAt = runtime.now().toISOString();
284
+ const steps = yield* WEB_SYNC_PLANS[kind].run(accountId, options, runtime);
277
285
 
278
286
  const backup = yield* maybeAutoSyncBackupEffect();
279
- const finishedAt = new Date().toISOString();
287
+ const finishedAt = runtime.now().toISOString();
280
288
  return {
281
289
  ok: true,
282
290
  kind,
@@ -290,12 +298,12 @@ export function performWebSyncEffect(
290
298
  });
291
299
  }
292
300
 
293
- function createWebSyncJobId(kind: WebSyncKind) {
294
- return `sync_${kind}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
301
+ function createWebSyncJobId(kind: WebSyncKind, runtime: ServerRuntimeServices) {
302
+ return `sync_${kind}_${runtime.now().getTime().toString(36)}_${runtime.random().toString(36).slice(2, 8)}`;
295
303
  }
296
304
 
297
- function resolveDefaultSyncAccountId() {
298
- const dbPath = getBirdclawPaths().dbPath;
305
+ function resolveDefaultSyncAccountId(runtime: ServerRuntimeServices) {
306
+ const dbPath = runtime.getPaths().dbPath;
299
307
  if (!existsSync(dbPath)) {
300
308
  return "acct_primary";
301
309
  }
@@ -336,12 +344,13 @@ function getRunningSyncKey(
336
344
  kind: WebSyncKind,
337
345
  accountId: string | undefined,
338
346
  options: WebSyncOptions = {},
347
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
339
348
  ) {
340
349
  if (!WEB_SYNC_PLANS[kind].accountAware) {
341
350
  const optionKey = serializeSyncOptions(kind, options);
342
351
  return optionKey ? `${kind}:${optionKey}` : kind;
343
352
  }
344
- return `${kind}:${accountId ?? resolveDefaultSyncAccountId()}`;
353
+ return `${kind}:${accountId ?? resolveDefaultSyncAccountId(runtime)}`;
345
354
  }
346
355
 
347
356
  function getEffectiveAccountId(
@@ -380,8 +389,9 @@ function toFailedResponse(
380
389
  startedAt: string,
381
390
  error: unknown,
382
391
  accountId?: string,
392
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
383
393
  ): WebSyncResponse {
384
- const finishedAt = new Date().toISOString();
394
+ const finishedAt = runtime.now().toISOString();
385
395
  const message = messageFromError(error);
386
396
  return {
387
397
  ok: false,
@@ -399,17 +409,18 @@ export function startWebSync(
399
409
  kind: WebSyncKind,
400
410
  accountId?: string,
401
411
  options: WebSyncOptions = {},
412
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
402
413
  ): WebSyncJobSnapshot {
403
414
  const effectiveAccountId = getEffectiveAccountId(kind, accountId);
404
- const syncKey = getRunningSyncKey(kind, effectiveAccountId, options);
415
+ const syncKey = getRunningSyncKey(kind, effectiveAccountId, options, runtime);
405
416
  const current = runningSyncs.get(syncKey);
406
417
  if (current) {
407
418
  return current;
408
419
  }
409
420
 
410
- const startedAt = new Date().toISOString();
421
+ const startedAt = runtime.now().toISOString();
411
422
  const job: WebSyncJobSnapshot = {
412
- id: createWebSyncJobId(kind),
423
+ id: createWebSyncJobId(kind, runtime),
413
424
  kind,
414
425
  ...(effectiveAccountId ? { accountId: effectiveAccountId } : {}),
415
426
  status: "running",
@@ -420,35 +431,39 @@ export function startWebSync(
420
431
  webSyncJobKeys.set(job.id, syncKey);
421
432
  setJobSnapshot(job);
422
433
 
423
- runEffectBackground(performWebSyncEffect(kind, effectiveAccountId, options), {
424
- onSuccess: (result) => {
425
- setJobSnapshot({
426
- ...job,
427
- status: "succeeded",
428
- finishedAt: result.finishedAt,
429
- summary: result.summary,
430
- inProgress: false,
431
- result,
432
- });
433
- },
434
- onFailure: (error) => {
435
- const result = toFailedResponse(
436
- kind,
437
- startedAt,
438
- error,
439
- effectiveAccountId,
440
- );
441
- setJobSnapshot({
442
- ...job,
443
- status: "failed",
444
- finishedAt: result.finishedAt,
445
- summary: result.summary,
446
- inProgress: false,
447
- result,
448
- error: result.error,
449
- });
434
+ runEffectBackground(
435
+ performWebSyncEffect(kind, effectiveAccountId, options, runtime),
436
+ {
437
+ onSuccess: (result) => {
438
+ setJobSnapshot({
439
+ ...job,
440
+ status: "succeeded",
441
+ finishedAt: result.finishedAt,
442
+ summary: result.summary,
443
+ inProgress: false,
444
+ result,
445
+ });
446
+ },
447
+ onFailure: (error) => {
448
+ const result = toFailedResponse(
449
+ kind,
450
+ startedAt,
451
+ error,
452
+ effectiveAccountId,
453
+ runtime,
454
+ );
455
+ setJobSnapshot({
456
+ ...job,
457
+ status: "failed",
458
+ finishedAt: result.finishedAt,
459
+ summary: result.summary,
460
+ inProgress: false,
461
+ result,
462
+ error: result.error,
463
+ });
464
+ },
450
465
  },
451
- });
466
+ );
452
467
 
453
468
  return job;
454
469
  }
@@ -461,13 +476,14 @@ export function runWebSyncEffect(
461
476
  kind: WebSyncKind,
462
477
  accountId?: string,
463
478
  options: WebSyncOptions = {},
479
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
464
480
  ): Effect.Effect<WebSyncResponse, Error> {
465
481
  return Effect.gen(function* () {
466
482
  const effectiveAccountId = getEffectiveAccountId(kind, accountId);
467
483
  const current = runningSyncs.get(
468
- getRunningSyncKey(kind, effectiveAccountId, options),
484
+ getRunningSyncKey(kind, effectiveAccountId, options, runtime),
469
485
  );
470
- const startedAt = new Date().toISOString();
486
+ const startedAt = runtime.now().toISOString();
471
487
  if (current) {
472
488
  return {
473
489
  ok: false,
@@ -480,7 +496,7 @@ export function runWebSyncEffect(
480
496
  } satisfies WebSyncResponse;
481
497
  }
482
498
 
483
- const job = startWebSync(kind, effectiveAccountId, options);
499
+ const job = startWebSync(kind, effectiveAccountId, options, runtime);
484
500
  while (job.inProgress) {
485
501
  yield* Effect.sleep(25);
486
502
  const latest = getWebSyncJob(job.id);
@@ -501,8 +517,9 @@ export function runWebSync(
501
517
  kind: WebSyncKind,
502
518
  accountId?: string,
503
519
  options: WebSyncOptions = {},
520
+ runtime: ServerRuntimeServices = defaultServerRuntimeServices,
504
521
  ): Promise<WebSyncResponse> {
505
- return runEffectPromise(runWebSyncEffect(kind, accountId, options));
522
+ return runEffectPromise(runWebSyncEffect(kind, accountId, options, runtime));
506
523
  }
507
524
 
508
525
  export function clearWebSyncLocksForTests() {
package/src/lib/whois.ts CHANGED
@@ -9,7 +9,8 @@ import { fetchProfileBioEntities } from "./profile-bio-entities";
9
9
  import { fetchProfileSnapshots } from "./profile-history";
10
10
  import { resolveProfilesForIdsEffect } from "./profile-resolver";
11
11
  import type { ProfileResolveResult } from "./profile-resolver";
12
- import { listDmConversations, listTimelineItems } from "./queries";
12
+ import { listDmConversations } from "./dm-read-model";
13
+ import { listTimelineItems } from "./timeline-read-model";
13
14
  import { expandUrlsFromTextsEffect } from "./url-expansion";
14
15
  import type {
15
16
  DmConversationItem,
package/src/router.tsx CHANGED
@@ -7,7 +7,7 @@ export function getRouter() {
7
7
 
8
8
  scrollRestoration: true,
9
9
  defaultPreload: "intent",
10
- defaultPreloadStaleTime: 0,
10
+ defaultPreloadStaleTime: 60_000,
11
11
  });
12
12
 
13
13
  return router;
@@ -1,13 +1,12 @@
1
- import { TanStackDevtools } from "@tanstack/react-devtools";
2
1
  import {
3
2
  createRootRoute,
4
3
  HeadContent,
5
4
  Scripts,
6
5
  useRouterState,
7
6
  } from "@tanstack/react-router";
8
- import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
9
7
  import type { ReactNode } from "react";
10
8
  import { AppNav } from "#/components/AppNav";
9
+ import { BirdclawQueryProvider } from "#/lib/query-client";
11
10
  import { ThemeProvider, themeScript } from "#/lib/theme";
12
11
  import {
13
12
  bodyClass,
@@ -66,25 +65,16 @@ function RootDocument({ children }: { children: ReactNode }) {
66
65
  <script suppressHydrationWarning>{themeScript}</script>
67
66
  </head>
68
67
  <body className={bodyClass}>
69
- <ThemeProvider>
70
- <div className={wideMode ? siteShellDmClass : siteShellClass}>
71
- <AppNav compact={wideMode} />
72
- <main className={wideMode ? mainColumnDmClass : mainColumnClass}>
73
- {children}
74
- </main>
75
- </div>
76
- </ThemeProvider>
77
- <TanStackDevtools
78
- config={{
79
- position: "bottom-right",
80
- }}
81
- plugins={[
82
- {
83
- name: "Tanstack Router",
84
- render: <TanStackRouterDevtoolsPanel />,
85
- },
86
- ]}
87
- />
68
+ <BirdclawQueryProvider>
69
+ <ThemeProvider>
70
+ <div className={wideMode ? siteShellDmClass : siteShellClass}>
71
+ <AppNav compact={wideMode} />
72
+ <main className={wideMode ? mainColumnDmClass : mainColumnClass}>
73
+ {children}
74
+ </main>
75
+ </div>
76
+ </ThemeProvider>
77
+ </BirdclawQueryProvider>
88
78
  <Scripts />
89
79
  </body>
90
80
  </html>
@@ -18,7 +18,7 @@ import {
18
18
  createDmReplyEffect,
19
19
  createPostEffect,
20
20
  createTweetReplyEffect,
21
- } from "#/lib/queries";
21
+ } from "#/lib/query-actions";
22
22
  import type { ActionsTransport } from "#/lib/config";
23
23
  import type { InboxKind } from "#/lib/types";
24
24
 
@@ -5,7 +5,7 @@ import {
5
5
  runRouteEffect,
6
6
  sensitiveRequestErrorResponse,
7
7
  } from "#/lib/http-effect";
8
- import { getTweetConversation } from "#/lib/queries";
8
+ import { getTweetConversation } from "#/lib/timeline-read-model";
9
9
 
10
10
  function json(data: unknown, status = 200) {
11
11
  return new Response(JSON.stringify(data), {
@@ -1,5 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { Effect } from "effect";
3
+ import { queryResponseSchema } from "#/lib/api-contracts";
3
4
  import { maybeAutoUpdateBackupEffect } from "#/lib/backup";
4
5
  import {
5
6
  jsonResponse,
@@ -7,7 +8,7 @@ import {
7
8
  runRouteEffect,
8
9
  sensitiveRequestErrorResponse,
9
10
  } from "#/lib/http-effect";
10
- import { queryResource } from "#/lib/queries";
11
+ import { queryResource } from "#/lib/query-resource";
11
12
  import type {
12
13
  DmQuery,
13
14
  ReplyFilter,
@@ -78,35 +79,40 @@ export const Route = createFileRoute("/api/query")({
78
79
 
79
80
  if (resource === "dms") {
80
81
  return jsonResponse(
81
- queryResource("dms", {
82
- ...baseFilters,
83
- participant: url.searchParams.get("participant") ?? undefined,
84
- minFollowers: parseOptionalNumber(
85
- url.searchParams.get("minFollowers"),
86
- ),
87
- maxFollowers: parseOptionalNumber(
88
- url.searchParams.get("maxFollowers"),
89
- ),
90
- minInfluenceScore: parseOptionalNumber(
91
- url.searchParams.get("minInfluenceScore"),
92
- ),
93
- maxInfluenceScore: parseOptionalNumber(
94
- url.searchParams.get("maxInfluenceScore"),
95
- ),
96
- sort: parseDmSort(url.searchParams.get("sort")),
97
- inbox: parseDmInbox(url.searchParams.get("inbox")),
98
- conversationId:
99
- url.searchParams.get("conversationId") ?? undefined,
100
- }),
82
+ queryResponseSchema.parse(
83
+ queryResource("dms", {
84
+ ...baseFilters,
85
+ participant:
86
+ url.searchParams.get("participant") ?? undefined,
87
+ minFollowers: parseOptionalNumber(
88
+ url.searchParams.get("minFollowers"),
89
+ ),
90
+ maxFollowers: parseOptionalNumber(
91
+ url.searchParams.get("maxFollowers"),
92
+ ),
93
+ minInfluenceScore: parseOptionalNumber(
94
+ url.searchParams.get("minInfluenceScore"),
95
+ ),
96
+ maxInfluenceScore: parseOptionalNumber(
97
+ url.searchParams.get("maxInfluenceScore"),
98
+ ),
99
+ sort: parseDmSort(url.searchParams.get("sort")),
100
+ inbox: parseDmInbox(url.searchParams.get("inbox")),
101
+ conversationId:
102
+ url.searchParams.get("conversationId") ?? undefined,
103
+ }),
104
+ ),
101
105
  );
102
106
  }
103
107
 
104
108
  return jsonResponse(
105
- queryResource(resource, {
106
- ...baseFilters,
107
- resource,
108
- untilId: url.searchParams.get("untilId") ?? undefined,
109
- }),
109
+ queryResponseSchema.parse(
110
+ queryResource(resource, {
111
+ ...baseFilters,
112
+ resource,
113
+ untilId: url.searchParams.get("untilId") ?? undefined,
114
+ }),
115
+ ),
110
116
  );
111
117
  }),
112
118
  ),
@@ -1,12 +1,13 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { Effect } from "effect";
3
+ import { queryEnvelopeSchema } from "#/lib/api-contracts";
3
4
  import { maybeAutoUpdateBackupEffect } from "#/lib/backup";
4
5
  import {
5
6
  jsonResponse,
6
7
  runRouteEffect,
7
8
  sensitiveRequestErrorResponse,
8
9
  } from "#/lib/http-effect";
9
- import { getQueryEnvelopeEffect } from "#/lib/queries";
10
+ import { getQueryEnvelopeEffect } from "#/lib/query-status";
10
11
 
11
12
  export const Route = createFileRoute("/api/status")({
12
13
  server: {
@@ -18,7 +19,10 @@ export const Route = createFileRoute("/api/status")({
18
19
  if (denied) return denied;
19
20
 
20
21
  yield* maybeAutoUpdateBackupEffect();
21
- return jsonResponse(yield* getQueryEnvelopeEffect());
22
+ const envelope = yield* getQueryEnvelopeEffect({
23
+ includeArchives: false,
24
+ });
25
+ return jsonResponse(queryEnvelopeSchema.parse(envelope));
22
26
  }),
23
27
  ),
24
28
  },
@@ -1,5 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { Effect } from "effect";
3
+ import { webSyncJobSchema } from "#/lib/api-contracts";
3
4
  import {
4
5
  jsonResponse,
5
6
  requestJsonEffect,
@@ -72,7 +73,7 @@ export const Route = createFileRoute("/api/sync")({
72
73
  );
73
74
  }
74
75
 
75
- return jsonResponse(job);
76
+ return jsonResponse(webSyncJobSchema.parse(job));
76
77
  },
77
78
  POST: ({ request }) =>
78
79
  runRouteEffect(
@@ -97,7 +98,9 @@ export const Route = createFileRoute("/api/sync")({
97
98
  parseAccountId(body.accountId),
98
99
  parseSyncOptions(kind, body),
99
100
  );
100
- return jsonResponse(job, { status: job.inProgress ? 202 : 200 });
101
+ return jsonResponse(webSyncJobSchema.parse(job), {
102
+ status: job.inProgress ? 202 : 200,
103
+ });
101
104
  }),
102
105
  ),
103
106
  },