birdclaw 0.8.2 → 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 (89) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +2 -1
  3. package/src/cli/command-context.ts +17 -0
  4. package/src/cli/register-analysis.ts +500 -0
  5. package/src/cli/register-compose.ts +40 -0
  6. package/src/cli/register-graph.ts +132 -0
  7. package/src/cli/register-inbox.ts +41 -0
  8. package/src/cli/register-storage.ts +106 -0
  9. package/src/cli.ts +30 -750
  10. package/src/components/AccountSwitcher.tsx +7 -15
  11. package/src/components/AvatarChip.tsx +1 -1
  12. package/src/components/AvatarPreload.ts +149 -0
  13. package/src/components/MarkdownCitations.tsx +680 -0
  14. package/src/components/MarkdownViewer.tsx +8 -674
  15. package/src/components/ProfileAnalysisClient.ts +191 -0
  16. package/src/components/ProfileAnalysisStream.tsx +16 -185
  17. package/src/components/ProfilePreview.tsx +2 -0
  18. package/src/components/links-controller.ts +162 -0
  19. package/src/components/links-model.ts +198 -0
  20. package/src/components/network-map-controller.ts +84 -0
  21. package/src/components/network-map-model.ts +255 -0
  22. package/src/components/useTimelineRouteData.ts +105 -235
  23. package/src/lib/analysis-runtime.ts +238 -0
  24. package/src/lib/api-client.ts +16 -215
  25. package/src/lib/api-contracts.ts +328 -0
  26. package/src/lib/archive-import-plan.ts +102 -0
  27. package/src/lib/archive-import.ts +170 -239
  28. package/src/lib/authored-live.ts +75 -120
  29. package/src/lib/backup.ts +335 -424
  30. package/src/lib/blocks-write.ts +30 -26
  31. package/src/lib/blocks.ts +18 -20
  32. package/src/lib/database-metrics.ts +88 -0
  33. package/src/lib/database-migrations.ts +34 -0
  34. package/src/lib/database-schema.ts +312 -0
  35. package/src/lib/database-writer.ts +69 -0
  36. package/src/lib/db.ts +84 -330
  37. package/src/lib/dm-read-model.ts +533 -0
  38. package/src/lib/dms-live.ts +34 -97
  39. package/src/lib/follow-graph.ts +17 -27
  40. package/src/lib/import-repository.ts +138 -0
  41. package/src/lib/inbox.ts +2 -1
  42. package/src/lib/live-sync-engine.ts +209 -0
  43. package/src/lib/live-transport-gateway.ts +128 -0
  44. package/src/lib/mention-threads-live.ts +90 -177
  45. package/src/lib/mentions-export.ts +1 -1
  46. package/src/lib/mentions-live.ts +57 -181
  47. package/src/lib/moderation-target.ts +15 -4
  48. package/src/lib/moderation-write.ts +1 -1
  49. package/src/lib/mutes-write.ts +30 -26
  50. package/src/lib/openai-response-runtime.ts +251 -0
  51. package/src/lib/paginated-sync.ts +93 -0
  52. package/src/lib/period-digest.ts +116 -304
  53. package/src/lib/profile-analysis.ts +36 -110
  54. package/src/lib/queries.ts +6 -2381
  55. package/src/lib/query-actions.ts +437 -0
  56. package/src/lib/query-client.tsx +47 -0
  57. package/src/lib/query-read-model-shared.ts +52 -0
  58. package/src/lib/query-read-models.ts +5 -0
  59. package/src/lib/query-resource.ts +41 -0
  60. package/src/lib/query-status.ts +164 -0
  61. package/src/lib/research.ts +1 -1
  62. package/src/lib/runtime-services.ts +20 -0
  63. package/src/lib/search-discussion.ts +75 -279
  64. package/src/lib/server-runtime-services.ts +30 -0
  65. package/src/lib/sqlite.ts +48 -12
  66. package/src/lib/streaming-ingestion.ts +240 -0
  67. package/src/lib/sync-cache.ts +6 -1
  68. package/src/lib/sync-plan.ts +175 -0
  69. package/src/lib/timeline-collections-live.ts +83 -257
  70. package/src/lib/timeline-live.ts +86 -236
  71. package/src/lib/timeline-read-model.ts +1191 -0
  72. package/src/lib/tweet-repository.ts +156 -0
  73. package/src/lib/tweet-search-live.ts +63 -167
  74. package/src/lib/web-sync.ts +67 -50
  75. package/src/lib/whois.ts +2 -1
  76. package/src/routes/__root.tsx +11 -8
  77. package/src/routes/api/action.tsx +1 -1
  78. package/src/routes/api/conversation.tsx +1 -1
  79. package/src/routes/api/query.tsx +32 -26
  80. package/src/routes/api/status.tsx +6 -4
  81. package/src/routes/api/sync.tsx +5 -2
  82. package/src/routes/blocks.tsx +97 -131
  83. package/src/routes/data-sources.tsx +17 -25
  84. package/src/routes/dms.tsx +167 -184
  85. package/src/routes/inbox.tsx +63 -57
  86. package/src/routes/links.tsx +31 -394
  87. package/src/routes/network-map.tsx +41 -344
  88. package/src/routes/rate-limits.tsx +17 -21
  89. package/src/lib/client-cache.ts +0 -109
@@ -1,28 +1,26 @@
1
1
  import type { Database } from "./sqlite";
2
2
  import { Effect } from "effect";
3
- import {
4
- getAuthenticatedBirdAccountEffect,
5
- listMentionsViaBirdEffect,
6
- } from "./bird";
7
3
  import type { MentionsDataSource } from "./config";
4
+ import { databaseWriteEffect } from "./database-writer";
8
5
  import { getNativeDb } from "./db";
9
- import { runEffectPromise, tryPromise } from "./effect-runtime";
10
- import { buildMediaJsonFromIncludes, countTweetMedia } from "./media-includes";
6
+ import { runEffectPromise } from "./effect-runtime";
7
+ import { liveTransportGateway } from "./live-transport-gateway";
8
+ import {
9
+ assertLiveAccountMatches,
10
+ resolveLiveSyncAccount,
11
+ type LiveSyncAccount,
12
+ } from "./live-sync-engine";
11
13
  import { serializeMentionItemsAsXurlCompatible } from "./mentions-export";
12
- import { listTimelineItems } from "./queries";
14
+ import { listTimelineItems } from "./timeline-read-model";
13
15
  import { deleteSyncCache, readSyncCache, writeSyncCache } from "./sync-cache";
14
- import { tweetEntitiesFromXurl } from "./tweet-render";
15
16
  import type {
16
17
  ReplyFilter,
17
- TweetEntities,
18
18
  XurlMentionData,
19
19
  XurlMentionsResponse,
20
20
  XurlMediaItem,
21
21
  XurlMentionUser,
22
22
  } from "./types";
23
- import { upsertTweetAccountEdge } from "./tweet-account-edges";
24
- import { ensureStubProfileForXUser, upsertProfileFromXUser } from "./x-profile";
25
- import { listMentionsViaXurl, lookupUsersByHandles } from "./xurl";
23
+ import { ingestTweetPayload } from "./tweet-repository";
26
24
 
27
25
  export const DEFAULT_MENTIONS_CACHE_TTL_MS = 2 * 60_000;
28
26
  const MIN_XURL_MENTIONS_LIMIT = 5;
@@ -431,71 +429,21 @@ function writeMentionHighWaterId(
431
429
  writeSyncCache(getMentionHighWaterKey({ mode, accountId }), { sinceId }, db);
432
430
  }
433
431
 
434
- function resolveAccount(db: Database, accountId?: string) {
435
- const row = accountId
436
- ? (db
437
- .prepare(
438
- "select id, handle, external_user_id from accounts where id = ?",
439
- )
440
- .get(accountId) as
441
- | { id: string; handle: string; external_user_id: string | null }
442
- | undefined)
443
- : (db
444
- .prepare(
445
- `
446
- select id, handle, external_user_id
447
- from accounts
448
- order by is_default desc, created_at asc
449
- limit 1
450
- `,
451
- )
452
- .get() as
453
- | { id: string; handle: string; external_user_id: string | null }
454
- | undefined);
455
-
456
- if (!row) {
457
- throw new Error(`Unknown account: ${accountId ?? "default"}`);
458
- }
459
-
460
- return {
461
- accountId: row.id,
462
- username: row.handle.replace(/^@/, ""),
463
- externalUserId:
464
- typeof row.external_user_id === "string" &&
465
- row.external_user_id.trim().length > 0
466
- ? row.external_user_id.trim()
467
- : undefined,
468
- };
469
- }
470
-
471
- function verifyBirdAccountMatchesEffect({
472
- accountId,
473
- username,
474
- externalUserId,
475
- }: ReturnType<typeof resolveAccount>) {
432
+ function verifyBirdAccountMatchesEffect(account: LiveSyncAccount) {
476
433
  return Effect.gen(function* () {
477
- const authenticated = yield* getAuthenticatedBirdAccountEffect();
478
- if (
479
- externalUserId &&
480
- authenticated.id &&
481
- authenticated.id === externalUserId
482
- ) {
483
- return;
484
- }
485
- if (externalUserId && authenticated.id) {
486
- return yield* Effect.fail(
487
- new Error(
488
- `bird is authenticated as user ${authenticated.id}; refusing to sync into ${accountId} (${externalUserId})`,
489
- ),
490
- );
491
- }
492
- if (authenticated.username.toLowerCase() !== username.toLowerCase()) {
493
- return yield* Effect.fail(
494
- new Error(
495
- `bird is authenticated as @${authenticated.username}; refusing to sync into ${accountId} (@${username})`,
496
- ),
497
- );
498
- }
434
+ const authenticated =
435
+ yield* liveTransportGateway.bird.getAuthenticatedAccount();
436
+ return yield* Effect.try({
437
+ try: () =>
438
+ assertLiveAccountMatches({
439
+ source: "bird",
440
+ account,
441
+ liveUsername: authenticated.username,
442
+ liveExternalUserId: authenticated.id,
443
+ }),
444
+ catch: (error) =>
445
+ error instanceof Error ? error : new Error(String(error)),
446
+ });
499
447
  });
500
448
  }
501
449
 
@@ -521,94 +469,20 @@ function findNewestArchiveMentionId(db: Database, accountId: string) {
521
469
  return row?.id;
522
470
  }
523
471
 
524
- function toLocalEntities(tweet: XurlMentionData): TweetEntities {
525
- return tweetEntitiesFromXurl(tweet.entities);
526
- }
527
-
528
- function replaceTweetFts(db: Database, tweetId: string, text: string) {
529
- db.prepare("delete from tweets_fts where tweet_id = ?").run(tweetId);
530
- db.prepare("insert into tweets_fts (tweet_id, text) values (?, ?)").run(
531
- tweetId,
532
- text,
533
- );
534
- }
535
-
536
472
  function mergeMentionsIntoLocalStore(
537
473
  db: Database,
538
474
  accountId: string,
539
475
  payload: XurlMentionsResponse,
540
476
  source: MentionsDataSource,
541
477
  ) {
542
- const usersById = new Map(
543
- (payload.includes?.users ?? []).map((user) => [user.id, user]),
544
- );
545
- const upsertTweet = db.prepare(
546
- `
547
- insert into tweets (
548
- id, account_id, author_profile_id, kind, text, created_at,
549
- is_replied, reply_to_id, like_count, media_count, bookmarked, liked,
550
- entities_json, media_json, quoted_tweet_id
551
- ) values (?, ?, ?, 'mention', ?, ?, 0, null, ?, ?, 0, 0, ?, ?, null)
552
- on conflict(id) do update set
553
- account_id = tweets.account_id,
554
- author_profile_id = excluded.author_profile_id,
555
- kind = case
556
- when tweets.kind in ('authored', 'home', 'mention') then tweets.kind
557
- when excluded.kind in ('authored', 'home', 'mention') then excluded.kind
558
- else excluded.kind
559
- end,
560
- text = excluded.text,
561
- created_at = excluded.created_at,
562
- like_count = excluded.like_count,
563
- media_count = max(tweets.media_count, excluded.media_count),
564
- entities_json = excluded.entities_json,
565
- media_json = case
566
- when excluded.media_json not in ('', '[]', 'null') then excluded.media_json
567
- else tweets.media_json
568
- end,
569
- is_replied = max(tweets.is_replied, excluded.is_replied),
570
- bookmarked = max(tweets.bookmarked, excluded.bookmarked),
571
- liked = max(tweets.liked, excluded.liked)
572
- `,
573
- );
574
-
575
- const writePayload = db.transaction(() => {
576
- const seenAt = new Date().toISOString();
577
- for (const tweet of payload.data) {
578
- const author =
579
- usersById.get(tweet.author_id) ??
580
- ({
581
- id: tweet.author_id,
582
- username: `user_${tweet.author_id}`,
583
- name: `user_${tweet.author_id}`,
584
- } as const);
585
- const profile = usersById.has(tweet.author_id)
586
- ? upsertProfileFromXUser(db, author)
587
- : ensureStubProfileForXUser(db, tweet.author_id);
588
- upsertTweet.run(
589
- tweet.id,
590
- accountId,
591
- profile.profile.id,
592
- tweet.text,
593
- tweet.created_at,
594
- Number(tweet.public_metrics?.like_count ?? 0),
595
- countTweetMedia(tweet),
596
- JSON.stringify(toLocalEntities(tweet)),
597
- buildMediaJsonFromIncludes(tweet, payload.includes?.media),
598
- );
599
- upsertTweetAccountEdge(db, {
600
- accountId,
601
- tweetId: tweet.id,
602
- kind: "mention",
603
- source,
604
- seenAt,
605
- rawJson: JSON.stringify(tweet),
606
- });
607
- replaceTweetFts(db, tweet.id, tweet.text);
608
- }
478
+ ingestTweetPayload(db, {
479
+ accountId,
480
+ payload,
481
+ kind: "mention",
482
+ edgeKind: "mention",
483
+ replaceSecondaryKind: true,
484
+ source,
609
485
  });
610
-
611
- writePayload();
612
486
  }
613
487
 
614
488
  function shouldReturnFilteredLocalPayload({
@@ -711,7 +585,7 @@ function fetchMentionsViaXurlEffect({
711
585
  startTime,
712
586
  onProgress,
713
587
  }: {
714
- resolvedAccount: ReturnType<typeof resolveAccount>;
588
+ resolvedAccount: LiveSyncAccount;
715
589
  limit: number;
716
590
  all: boolean;
717
591
  parsedMaxPages: number | null;
@@ -723,9 +597,9 @@ function fetchMentionsViaXurlEffect({
723
597
  return Effect.gen(function* () {
724
598
  const accountUserId =
725
599
  resolvedAccount.externalUserId ??
726
- (yield* tryPromise(() =>
727
- lookupUsersByHandles([resolvedAccount.username]),
728
- ).pipe(Effect.map((users) => users[0]?.id)));
600
+ (yield* liveTransportGateway.xurl
601
+ .lookupUsersByHandles([resolvedAccount.username])
602
+ .pipe(Effect.map((users) => users[0]?.id)));
729
603
  if (!accountUserId) {
730
604
  return yield* Effect.fail(
731
605
  new Error(
@@ -738,16 +612,14 @@ function fetchMentionsViaXurlEffect({
738
612
  let nextToken: string | undefined = startPaginationToken;
739
613
  let pageCount = 0;
740
614
  do {
741
- const payload = yield* tryPromise(() =>
742
- listMentionsViaXurl({
743
- maxResults: limit,
744
- username: resolvedAccount.username,
745
- userId: String(accountUserId),
746
- paginationToken: nextToken,
747
- ...(sinceId ? { sinceId } : {}),
748
- ...(startTime ? { startTime } : {}),
749
- }),
750
- );
615
+ const payload = yield* liveTransportGateway.xurl.listMentions({
616
+ maxResults: limit,
617
+ username: resolvedAccount.username,
618
+ userId: String(accountUserId),
619
+ paginationToken: nextToken,
620
+ ...(sinceId ? { sinceId } : {}),
621
+ ...(startTime ? { startTime } : {}),
622
+ });
751
623
  pages.push(payload);
752
624
  const metaNextToken =
753
625
  typeof payload.meta?.next_token === "string"
@@ -782,7 +654,7 @@ function fetchMentionsViaXurlEffect({
782
654
  }
783
655
 
784
656
  function fetchMentionsViaBirdEffect({ limit }: { limit: number }) {
785
- return listMentionsViaBirdEffect({ maxResults: limit });
657
+ return liveTransportGateway.bird.listMentions({ maxResults: limit });
786
658
  }
787
659
 
788
660
  function isMaxPagesPartial({
@@ -832,7 +704,9 @@ export function syncMentionsEffect({
832
704
  (parsedMaxPages !== null ||
833
705
  Boolean(explicitSinceId || explicitStartTime));
834
706
  const db = yield* trySync(() => getNativeDb());
835
- const resolvedAccount = yield* trySync(() => resolveAccount(db, account));
707
+ const resolvedAccount = yield* trySync(() =>
708
+ resolveLiveSyncAccount(db, account),
709
+ );
836
710
  const cursorShape: MentionScanShape = {
837
711
  endpoint: "mentions",
838
712
  mode: primaryMode,
@@ -922,9 +796,9 @@ export function syncMentionsEffect({
922
796
  cached &&
923
797
  cacheAgeMs <= ttlMs
924
798
  ) {
925
- yield* trySync(() =>
799
+ yield* databaseWriteEffect((writeDb) =>
926
800
  mergeMentionsIntoLocalStore(
927
- db,
801
+ writeDb,
928
802
  resolvedAccount.accountId,
929
803
  cached.value,
930
804
  primaryMode,
@@ -1002,9 +876,9 @@ export function syncMentionsEffect({
1002
876
  }),
1003
877
  );
1004
878
  }
1005
- yield* trySync(() =>
879
+ yield* databaseWriteEffect((writeDb) =>
1006
880
  mergeMentionsIntoLocalStore(
1007
- db,
881
+ writeDb,
1008
882
  resolvedAccount.accountId,
1009
883
  payload,
1010
884
  source,
@@ -1113,7 +987,9 @@ function exportMentionsViaCachedLiveSourceEffect({
1113
987
  const fetchAll = primaryMode === "xurl" && (all || parsedMaxPages !== null);
1114
988
 
1115
989
  const db = yield* trySync(() => getNativeDb());
1116
- const resolvedAccount = yield* trySync(() => resolveAccount(db, account));
990
+ const resolvedAccount = yield* trySync(() =>
991
+ resolveLiveSyncAccount(db, account),
992
+ );
1117
993
  const cacheKey = getMentionsFetchModeKey({
1118
994
  scope: "export",
1119
995
  mode,
@@ -1171,14 +1047,14 @@ function exportMentionsViaCachedLiveSourceEffect({
1171
1047
  );
1172
1048
  }),
1173
1049
  Effect.flatMap((payload) =>
1174
- trySync(() => {
1050
+ databaseWriteEffect((writeDb) => {
1175
1051
  mergeMentionsIntoLocalStore(
1176
- db,
1052
+ writeDb,
1177
1053
  resolvedAccount.accountId,
1178
1054
  payload,
1179
1055
  source,
1180
1056
  );
1181
- writeSyncCache(cacheKey, payload, db);
1057
+ writeSyncCache(cacheKey, payload, writeDb);
1182
1058
  return readFilteredOrRaw(payload);
1183
1059
  }),
1184
1060
  ),
@@ -2,6 +2,7 @@ import type { Database } from "./sqlite";
2
2
  import { Effect } from "effect";
3
3
  import { lookupProfileViaBird } from "./bird-actions";
4
4
  import { getNativeDb } from "./db";
5
+ import { databaseWriteEffect } from "./database-writer";
5
6
  import { runEffectPromise, tryPromise } from "./effect-runtime";
6
7
  import type { ProfileRecord, XurlMentionUser } from "./types";
7
8
  import { getExternalUserId, upsertProfileFromXUser } from "./x-profile";
@@ -105,9 +106,10 @@ export function resolveLocalProfile(
105
106
 
106
107
  export function resolveProfileEffect(
107
108
  query: string,
109
+ providedDb?: Database,
108
110
  ): Effect.Effect<ResolvedModerationProfile, unknown> {
109
111
  return Effect.gen(function* () {
110
- const db = yield* trySync(() => getNativeDb());
112
+ const db = providedDb ?? (yield* trySync(() => getNativeDb()));
111
113
  const normalizedQuery = normalizeProfileQuery(query);
112
114
  if (!normalizedQuery) {
113
115
  return yield* Effect.fail(new Error("Missing profile handle or id"));
@@ -180,7 +182,10 @@ export function resolveProfileEffect(
180
182
  }
181
183
 
182
184
  if (local) {
183
- return yield* trySync(() => upsertProfileFromXUser(db, user));
185
+ return yield* databaseWriteEffect(
186
+ (writeDb) => upsertProfileFromXUser(writeDb, user),
187
+ db,
188
+ );
184
189
  }
185
190
 
186
191
  const username = String(user.username ?? "").replace(/^@/, "");
@@ -189,11 +194,17 @@ export function resolveProfileEffect(
189
194
  resolveLocalProfile(db, username),
190
195
  );
191
196
  if (localByHandle) {
192
- return yield* trySync(() => upsertProfileFromXUser(db, user));
197
+ return yield* databaseWriteEffect(
198
+ (writeDb) => upsertProfileFromXUser(writeDb, user),
199
+ db,
200
+ );
193
201
  }
194
202
  }
195
203
 
196
- return yield* trySync(() => upsertProfileFromXUser(db, user));
204
+ return yield* databaseWriteEffect(
205
+ (writeDb) => upsertProfileFromXUser(writeDb, user),
206
+ db,
207
+ );
197
208
  });
198
209
  }
199
210
 
@@ -53,7 +53,7 @@ export function resolveModerationTargetEffect({
53
53
  return yield* Effect.fail(new Error(selfActionError));
54
54
  }
55
55
 
56
- const resolved = yield* resolveProfileEffect(query);
56
+ const resolved = yield* resolveProfileEffect(query, db);
57
57
  const account = yield* trySync(
58
58
  () =>
59
59
  db
@@ -1,5 +1,6 @@
1
1
  import { Effect } from "effect";
2
2
  import { runModerationAction } from "./actions-transport";
3
+ import { databaseWriteEffect } from "./database-writer";
3
4
  import { runEffectPromise, tryPromise } from "./effect-runtime";
4
5
  import {
5
6
  deleteModerationRow,
@@ -8,14 +9,6 @@ import {
8
9
  writeModerationRow,
9
10
  } from "./moderation-write";
10
11
 
11
- function trySync<T>(try_: () => T) {
12
- return Effect.try({
13
- try: try_,
14
- catch: (error) =>
15
- error instanceof Error ? error : new Error(String(error)),
16
- });
17
- }
18
-
19
12
  export function addMuteEffect(
20
13
  accountId: string,
21
14
  query: string,
@@ -49,14 +42,16 @@ export function addMuteEffect(
49
42
  }
50
43
 
51
44
  const mutedAt = new Date().toISOString();
52
- yield* trySync(() =>
53
- writeModerationRow(
54
- db,
55
- "mutes",
56
- resolvedAccountId,
57
- resolved.profile.id,
58
- mutedAt,
59
- ),
45
+ yield* databaseWriteEffect(
46
+ (writeDb) =>
47
+ writeModerationRow(
48
+ writeDb,
49
+ "mutes",
50
+ resolvedAccountId,
51
+ resolved.profile.id,
52
+ mutedAt,
53
+ ),
54
+ db,
60
55
  );
61
56
 
62
57
  return {
@@ -88,14 +83,16 @@ export function recordMuteEffect(accountId: string, query: string) {
88
83
  });
89
84
 
90
85
  const mutedAt = new Date().toISOString();
91
- yield* trySync(() =>
92
- writeModerationRow(
93
- db,
94
- "mutes",
95
- resolvedAccountId,
96
- resolved.profile.id,
97
- mutedAt,
98
- ),
86
+ yield* databaseWriteEffect(
87
+ (writeDb) =>
88
+ writeModerationRow(
89
+ writeDb,
90
+ "mutes",
91
+ resolvedAccountId,
92
+ resolved.profile.id,
93
+ mutedAt,
94
+ ),
95
+ db,
99
96
  );
100
97
 
101
98
  return {
@@ -144,8 +141,15 @@ export function removeMuteEffect(
144
141
  };
145
142
  }
146
143
 
147
- yield* trySync(() =>
148
- deleteModerationRow(db, "mutes", resolvedAccountId, resolved.profile.id),
144
+ yield* databaseWriteEffect(
145
+ (writeDb) =>
146
+ deleteModerationRow(
147
+ writeDb,
148
+ "mutes",
149
+ resolvedAccountId,
150
+ resolved.profile.id,
151
+ ),
152
+ db,
149
153
  );
150
154
 
151
155
  return {