@agent-native/core 0.133.2 → 0.133.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 (58) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/use-action.ts +6 -0
  5. package/corpus/templates/content/actions/_batch-utils.ts +33 -0
  6. package/corpus/templates/content/actions/_builder-cms-read-client.ts +173 -81
  7. package/corpus/templates/content/actions/_content-files.ts +31 -21
  8. package/corpus/templates/content/actions/_database-source-utils.ts +653 -155
  9. package/corpus/templates/content/actions/_database-utils.ts +535 -41
  10. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +291 -240
  11. package/corpus/templates/content/actions/add-database-item.ts +15 -1
  12. package/corpus/templates/content/actions/attach-content-database-source.ts +156 -58
  13. package/corpus/templates/content/actions/bind-content-database-source-field.ts +2 -2
  14. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +4 -1
  15. package/corpus/templates/content/actions/delete-database-items.ts +8 -2
  16. package/corpus/templates/content/actions/disconnect-content-database-source.ts +2 -2
  17. package/corpus/templates/content/actions/duplicate-database-item.ts +15 -1
  18. package/corpus/templates/content/actions/duplicate-database-items.ts +19 -1
  19. package/corpus/templates/content/actions/execute-builder-source-execution.ts +2 -1
  20. package/corpus/templates/content/actions/get-content-database.ts +12 -64
  21. package/corpus/templates/content/actions/move-database-item.ts +4 -1
  22. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +1 -1
  23. package/corpus/templates/content/actions/prepare-builder-source-review.ts +4 -8
  24. package/corpus/templates/content/actions/preview-content-database-source-attach.ts +92 -0
  25. package/corpus/templates/content/actions/process-builder-body-hydration.ts +2 -1
  26. package/corpus/templates/content/actions/query-content-database-items.ts +64 -0
  27. package/corpus/templates/content/actions/refresh-content-database-source.ts +7 -0
  28. package/corpus/templates/content/actions/review-content-database-source-change-set.ts +1 -1
  29. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +1 -1
  30. package/corpus/templates/content/actions/stage-builder-revision.ts +1 -1
  31. package/corpus/templates/content/actions/update-content-database-personal-view.ts +29 -3
  32. package/corpus/templates/content/actions/update-content-database-view.ts +1 -1
  33. package/corpus/templates/content/actions/validate-builder-source-execution.ts +1 -1
  34. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +3 -22
  35. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +267 -105
  36. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +82 -1
  37. package/corpus/templates/content/app/hooks/use-content-database.ts +285 -24
  38. package/corpus/templates/content/app/hooks/use-document-properties.ts +7 -6
  39. package/corpus/templates/content/app/i18n-data.ts +10 -0
  40. package/corpus/templates/content/changelog/2026-07-29-large-databases-keep-useful-rows-visible.md +6 -0
  41. package/corpus/templates/content/changelog/2026-07-30-builder-source-columns-now-appear-immediately-when-connected.md +6 -0
  42. package/corpus/templates/content/changelog/2026-07-30-large-builder-backed-tables-show-useful-rows-sooner-and-fini.md +6 -0
  43. package/corpus/templates/content/changelog/2026-08-01-large-builder-databases-now-show-rows-immediately-and-finish.md +6 -0
  44. package/corpus/templates/content/parity/matrix.md +2 -1
  45. package/corpus/templates/content/parity/matrix.ts +25 -0
  46. package/corpus/templates/content/shared/api.ts +54 -4
  47. package/corpus/templates/content/shared/database-query.ts +359 -0
  48. package/dist/client/use-action.d.ts.map +1 -1
  49. package/dist/client/use-action.js +6 -0
  50. package/dist/client/use-action.js.map +1 -1
  51. package/dist/notifications/routes.d.ts +1 -1
  52. package/dist/observability/routes.d.ts +3 -3
  53. package/dist/progress/routes.d.ts +1 -1
  54. package/dist/resources/handlers.d.ts +1 -1
  55. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  56. package/dist/server/transcribe-voice.d.ts +1 -1
  57. package/package.json +1 -1
  58. package/src/client/use-action.ts +6 -0
@@ -321,10 +321,80 @@ function withPersonalSidebarOrder(
321
321
  };
322
322
  }
323
323
 
324
+ const INITIAL_EXPANDED_WORKSPACE_READ_DELAY_MS = 250;
325
+ const DATABASE_PAGE_READY_FALLBACK_MS = 15_000;
326
+ const DATABASE_ROWS_VISIBLE_EVENT = "content-database-rows-visible";
327
+
328
+ function useDeferredFilesDatabaseId(
329
+ databaseId: string,
330
+ expanded: boolean,
331
+ deferUntilDocumentId: string | null,
332
+ ) {
333
+ const previouslyExpanded = useRef(expanded);
334
+ const [ready, setReady] = useState(false);
335
+
336
+ useEffect(() => {
337
+ const wasExpanded = previouslyExpanded.current;
338
+ previouslyExpanded.current = expanded;
339
+ if (!expanded) {
340
+ setReady(false);
341
+ return;
342
+ }
343
+ if (!wasExpanded) {
344
+ setReady(true);
345
+ return;
346
+ }
347
+
348
+ if (deferUntilDocumentId) {
349
+ if (
350
+ window.document.documentElement.dataset
351
+ .contentDatabaseRowsVisibleDocumentId === deferUntilDocumentId
352
+ ) {
353
+ setReady(true);
354
+ return;
355
+ }
356
+ setReady(false);
357
+ const handleRowsVisible = (event: Event) => {
358
+ if (
359
+ (event as CustomEvent<{ documentId?: string }>).detail?.documentId ===
360
+ deferUntilDocumentId
361
+ ) {
362
+ setReady(true);
363
+ }
364
+ };
365
+ window.addEventListener(DATABASE_ROWS_VISIBLE_EVENT, handleRowsVisible);
366
+ const fallback = window.setTimeout(
367
+ () => setReady(true),
368
+ DATABASE_PAGE_READY_FALLBACK_MS,
369
+ );
370
+ return () => {
371
+ window.removeEventListener(
372
+ DATABASE_ROWS_VISIBLE_EVENT,
373
+ handleRowsVisible,
374
+ );
375
+ window.clearTimeout(fallback);
376
+ };
377
+ }
378
+
379
+ // An already-expanded workspace can contain thousands of files. Give the
380
+ // selected page's critical read one turn before starting that inventory;
381
+ // direct expansion remains immediate.
382
+ setReady(false);
383
+ const timeout = window.setTimeout(
384
+ () => setReady(true),
385
+ INITIAL_EXPANDED_WORKSPACE_READ_DELAY_MS,
386
+ );
387
+ return () => window.clearTimeout(timeout);
388
+ }, [databaseId, deferUntilDocumentId, expanded]);
389
+
390
+ return expanded && ready ? databaseId : null;
391
+ }
392
+
324
393
  function WorkspaceSidebarItem({
325
394
  space,
326
395
  selected,
327
396
  expanded,
397
+ deferInitialReadUntilDocumentId,
328
398
  reorder,
329
399
  createDocumentPending,
330
400
  activeDocumentId,
@@ -341,6 +411,7 @@ function WorkspaceSidebarItem({
341
411
  space: ContentSpaceSummary;
342
412
  selected: boolean;
343
413
  expanded: boolean;
414
+ deferInitialReadUntilDocumentId: string | null;
344
415
  reorder?: ContentFilesSidebarRenderReorder;
345
416
  createDocumentPending: boolean;
346
417
  activeDocumentId: string | null;
@@ -361,7 +432,11 @@ function WorkspaceSidebarItem({
361
432
  onToggleFavorite: (item: ContentDatabaseItem) => void;
362
433
  }) {
363
434
  const t = useT();
364
- const activeFilesDatabaseId = expanded ? space.filesDatabaseId : null;
435
+ const activeFilesDatabaseId = useDeferredFilesDatabaseId(
436
+ space.filesDatabaseId,
437
+ expanded,
438
+ deferInitialReadUntilDocumentId,
439
+ );
365
440
  const filesDatabase = useContentDatabaseById(activeFilesDatabaseId);
366
441
  const filesDatabaseData = isContentDatabaseUnavailable(filesDatabase.data)
367
442
  ? undefined
@@ -1828,6 +1903,12 @@ export function DocumentSidebar({
1828
1903
  space={space}
1829
1904
  selected={selectedSpace?.id === space.id}
1830
1905
  expanded={expandedWorkspaceIds.includes(space.id)}
1906
+ deferInitialReadUntilDocumentId={
1907
+ activeDocumentId &&
1908
+ databaseDocuments.some((document) => document.id === activeDocumentId)
1909
+ ? activeDocumentId
1910
+ : null
1911
+ }
1831
1912
  reorder={reorder}
1832
1913
  createDocumentPending={createDocument.isPending}
1833
1914
  activeDocumentId={activeDocumentId}
@@ -6,11 +6,16 @@ import type {
6
6
  AddContentDatabaseSourceFieldPropertyRequest,
7
7
  AddDatabaseItemRequest,
8
8
  AttachContentDatabaseSourceRequest,
9
+ BuilderCmsAttachPreviewResponse,
9
10
  BuilderCmsModelsResponse,
10
11
  CancelPreparedBuilderSourceUpdateRequest,
11
12
  CancelPreparedBuilderSourceUpdateResponse,
12
13
  ChangeContentDatabaseSourceRoleRequest,
13
14
  ContentDatabaseResponse,
15
+ ContentDatabaseSourceAttachmentAck,
16
+ ContentDatabaseSourceAttachmentResult,
17
+ ContentDatabaseItemsPageResponse,
18
+ ContentDatabaseTableQuery,
14
19
  ContentDatabaseItem,
15
20
  ContentDatabasePersonalViewResponse,
16
21
  ContentDatabaseSourceFieldMapping,
@@ -60,6 +65,11 @@ export function contentDatabaseByIdQueryKey(databaseId: string) {
60
65
  return ["action", "get-content-database", { databaseId }] as const;
61
66
  }
62
67
 
68
+ export const contentDatabaseItemsPageQueryKey = [
69
+ "action",
70
+ "query-content-database-items",
71
+ ] as const;
72
+
63
73
  export function applyOptimisticItemToContentDatabase(
64
74
  current: ContentDatabaseResponse | undefined,
65
75
  item: ContentDatabaseItem,
@@ -174,21 +184,59 @@ export function contentDatabaseQueryFilter(documentId: string) {
174
184
  };
175
185
  }
176
186
 
187
+ export function contentDatabaseConstrainedQueryFilter(documentId: string) {
188
+ return {
189
+ queryKey: ["action", "get-content-database"],
190
+ predicate: (query: Query) => {
191
+ if (!isContentDatabaseQueryForDocument(query.queryKey, documentId)) {
192
+ return false;
193
+ }
194
+ const params = query.queryKey[2] as { tableQuery?: unknown };
195
+ return params.tableQuery !== undefined;
196
+ },
197
+ };
198
+ }
199
+
177
200
  export function writeContentDatabaseResponseToCache(
178
201
  queryClient: Pick<QueryClient, "setQueryData" | "setQueriesData">,
179
202
  documentId: string,
180
203
  data: ContentDatabaseResponse,
181
204
  ) {
182
- queryClient.setQueryData<ContentDatabaseResponse>(
183
- contentDatabaseQueryKey(documentId),
184
- data,
185
- );
205
+ if (!data.pagination) {
206
+ queryClient.setQueryData<ContentDatabaseResponse>(
207
+ contentDatabaseQueryKey(documentId),
208
+ data,
209
+ );
210
+ }
186
211
  queryClient.setQueriesData<ContentDatabaseResponse>(
187
- contentDatabaseQueryFilter(documentId),
212
+ {
213
+ queryKey: ["action", "get-content-database"],
214
+ predicate: (query) =>
215
+ contentDatabaseResponseCanSeedQuery(query.queryKey, documentId, data),
216
+ },
188
217
  data,
189
218
  );
190
219
  }
191
220
 
221
+ export function contentDatabaseResponseCanSeedQuery(
222
+ queryKey: readonly unknown[],
223
+ documentId: string,
224
+ data: ContentDatabaseResponse,
225
+ ) {
226
+ if (!isContentDatabaseQueryForDocument(queryKey, documentId)) return false;
227
+ const params = queryKey[2] as {
228
+ limit?: unknown;
229
+ offset?: unknown;
230
+ tableQuery?: unknown;
231
+ };
232
+ if (params.tableQuery !== undefined) return false;
233
+ if (!data.pagination) return params.limit === undefined;
234
+ return (
235
+ params.limit === data.pagination.limit &&
236
+ (params.offset ?? 0) === data.pagination.offset
237
+ );
238
+ }
239
+
192
240
  export function applyOptimisticBuilderWriteMode(
193
241
  current: ContentDatabaseResponse | undefined,
194
242
  request: SetContentDatabaseSourceWriteModeRequest,
@@ -583,9 +631,13 @@ function removeOptimisticSourceFieldProperty(
583
631
  };
584
632
  }
585
633
 
586
- export function useContentDatabase(documentId: string | null, limit?: number) {
634
+ export function useContentDatabase(
635
+ documentId: string | null,
636
+ limit?: number,
637
+ tableQuery?: ContentDatabaseTableQuery,
638
+ ) {
587
639
  const queryClient = useQueryClient();
588
- return useActionQuery<ContentDatabaseResponse>(
640
+ const baseQuery = useActionQuery<ContentDatabaseResponse>(
589
641
  "get-content-database",
590
642
  documentId ? { documentId, limit } : undefined,
591
643
  {
@@ -604,6 +656,39 @@ export function useContentDatabase(documentId: string | null, limit?: number) {
604
656
  initialDataUpdatedAt: 0,
605
657
  },
606
658
  );
659
+ const pageQuery = useActionQuery<ContentDatabaseItemsPageResponse>(
660
+ "query-content-database-items",
661
+ documentId && tableQuery ? { documentId, limit, tableQuery } : undefined,
662
+ {
663
+ enabled: Boolean(documentId && tableQuery),
664
+ retry: false,
665
+ placeholderData: (previous) => previous,
666
+ },
667
+ );
668
+ const page = tableQuery ? pageQuery.data : undefined;
669
+ const data =
670
+ page &&
671
+ baseQuery.data &&
672
+ !baseQuery.data.attachPreview &&
673
+ !isContentDatabaseUnavailable(baseQuery.data)
674
+ ? {
675
+ ...baseQuery.data,
676
+ items: page.items,
677
+ source: page.source,
678
+ sources: page.sources,
679
+ pagination: page.pagination,
680
+ tableQueryMode: page.tableQueryMode,
681
+ }
682
+ : baseQuery.data;
683
+ return {
684
+ ...baseQuery,
685
+ data,
686
+ isLoading: tableQuery ? pageQuery.isLoading && !data : baseQuery.isLoading,
687
+ isFetching: tableQuery ? pageQuery.isFetching : baseQuery.isFetching,
688
+ isError: tableQuery ? pageQuery.isError : baseQuery.isError,
689
+ error: tableQuery ? pageQuery.error : baseQuery.error,
690
+ refetch: tableQuery ? pageQuery.refetch : baseQuery.refetch,
691
+ };
607
692
  }
608
693
 
609
694
  export function useContentDatabaseById(databaseId: string | null) {
@@ -749,12 +834,28 @@ export function useAddDatabaseItem(documentId: string) {
749
834
  return useActionMutation<ContentDatabaseResponse, AddDatabaseItemRequest>(
750
835
  "add-database-item",
751
836
  {
837
+ skipActionQueryInvalidation: true,
752
838
  onSuccess: (data) => {
753
- // The action returns the committed row and full database snapshot.
754
- // Seed every active pagination key before invalidating so navigating
755
- // away from the creation side-peek cannot briefly lose an appended row
756
- // behind an older 100/200-row response.
757
- writeContentDatabaseResponseToCache(queryClient, documentId, data);
839
+ if (data.createdItem) {
840
+ queryClient.setQueriesData<ContentDatabaseResponse>(
841
+ {
842
+ queryKey: ["action", "get-content-database"],
843
+ predicate: (query) => {
844
+ if (
845
+ !isContentDatabaseQueryForDocument(query.queryKey, documentId)
846
+ ) {
847
+ return false;
848
+ }
849
+ const params = query.queryKey[2] as {
850
+ tableQuery?: unknown;
851
+ };
852
+ return params.tableQuery === undefined;
853
+ },
854
+ },
855
+ (current) =>
856
+ applyOptimisticItemToContentDatabase(current, data.createdItem!),
857
+ );
858
+ }
758
859
  queryClient.invalidateQueries({
759
860
  queryKey: contentDatabaseQueryKey(documentId),
760
861
  });
@@ -779,6 +880,9 @@ export function useSubmitContentDatabaseForm(documentId: string) {
779
880
  queryClient.invalidateQueries({
780
881
  queryKey: contentDatabaseQueryKey(documentId),
781
882
  });
883
+ queryClient.invalidateQueries({
884
+ queryKey: contentDatabaseItemsPageQueryKey,
885
+ });
782
886
  queryClient.invalidateQueries({
783
887
  queryKey: ["action", "list-documents"],
784
888
  });
@@ -796,6 +900,9 @@ export function useDuplicateDatabaseItem(documentId: string) {
796
900
  queryClient.invalidateQueries({
797
901
  queryKey: contentDatabaseQueryKey(documentId),
798
902
  });
903
+ queryClient.invalidateQueries({
904
+ queryKey: contentDatabaseItemsPageQueryKey,
905
+ });
799
906
  queryClient.invalidateQueries({
800
907
  queryKey: ["action", "list-documents"],
801
908
  });
@@ -912,13 +1019,18 @@ export function useUpdateContentDatabaseView(documentId: string) {
912
1019
  ContentDatabaseResponse,
913
1020
  UpdateContentDatabaseViewRequest
914
1021
  >("update-content-database-view", {
915
- onSuccess: () => {
916
- queryClient.invalidateQueries({
917
- queryKey: contentDatabaseQueryKey(documentId),
918
- });
919
- queryClient.invalidateQueries({
920
- queryKey: ["action", "get-content-database-source", { documentId }],
921
- });
1022
+ skipActionQueryInvalidation: true,
1023
+ onSuccess: (data) => {
1024
+ queryClient.setQueriesData<ContentDatabaseResponse>(
1025
+ contentDatabaseQueryFilter(documentId),
1026
+ (current) =>
1027
+ current
1028
+ ? {
1029
+ ...current,
1030
+ database: data.database,
1031
+ }
1032
+ : current,
1033
+ );
922
1034
  },
923
1035
  });
924
1036
  }
@@ -991,17 +1103,76 @@ export function useUpdateContentDatabasePersonalView(
991
1103
  });
992
1104
  }
993
1105
 
994
- export function useAttachContentDatabaseSource(documentId: string) {
1106
+ export function useAttachContentDatabaseSource(
1107
+ documentId: string,
1108
+ fallbackData?: ContentDatabaseResponse,
1109
+ ) {
995
1110
  const queryClient = useQueryClient();
996
1111
  return useActionMutation<
997
- ContentDatabaseResponse,
1112
+ ContentDatabaseSourceAttachmentResult,
998
1113
  AttachContentDatabaseSourceRequest
999
1114
  >("attach-content-database-source", {
1115
+ skipActionQueryInvalidation: true,
1116
+ onMutate: async (variables) => {
1117
+ const previous = queryClient.getQueriesData<ContentDatabaseResponse>(
1118
+ contentDatabaseQueryFilter(documentId),
1119
+ );
1120
+ const cancelPending = queryClient.cancelQueries(
1121
+ contentDatabaseQueryFilter(documentId),
1122
+ { revert: false },
1123
+ );
1124
+ if (variables.sourceType === "builder-cms" && variables.sourceTable) {
1125
+ const preview =
1126
+ queryClient.getQueryData<BuilderCmsAttachPreviewResponse>([
1127
+ "action",
1128
+ "preview-content-database-source-attach",
1129
+ {
1130
+ documentId,
1131
+ sourceTable: variables.sourceTable,
1132
+ fieldPaths: variables.builderFieldPaths,
1133
+ },
1134
+ ]);
1135
+ if (preview) {
1136
+ writeBuilderAttachPreviewToCache(
1137
+ queryClient,
1138
+ documentId,
1139
+ preview,
1140
+ fallbackData,
1141
+ );
1142
+ }
1143
+ }
1144
+ await cancelPending;
1145
+ return { previous };
1146
+ },
1147
+ onError: (_error, _variables, context) => {
1148
+ const previous = (
1149
+ context as
1150
+ | {
1151
+ previous?: Array<
1152
+ [readonly unknown[], ContentDatabaseResponse | undefined]
1153
+ >;
1154
+ }
1155
+ | undefined
1156
+ )?.previous;
1157
+ for (const [queryKey, data] of previous ?? []) {
1158
+ queryClient.setQueryData(queryKey, data);
1159
+ }
1160
+ },
1000
1161
  onSuccess: (data) => {
1001
- writeContentDatabaseResponseToCache(queryClient, documentId, data);
1162
+ if (!("responseProjection" in data)) {
1163
+ writeContentDatabaseResponseToCache(queryClient, documentId, data);
1164
+ } else {
1165
+ queryClient.setQueriesData<ContentDatabaseResponse>(
1166
+ contentDatabaseQueryFilter(documentId),
1167
+ (current) => applyBuilderAttachCompletion(current, data),
1168
+ );
1169
+ }
1002
1170
  queryClient.invalidateQueries({
1003
1171
  queryKey: contentDatabaseQueryKey(documentId),
1004
1172
  });
1173
+ queryClient.invalidateQueries({
1174
+ queryKey: contentDatabaseItemsPageQueryKey,
1175
+ });
1005
1176
  queryClient.invalidateQueries({
1006
1177
  queryKey: ["action", "get-content-database-source", { documentId }],
1007
1178
  });
@@ -1009,6 +1180,84 @@ export function useAttachContentDatabaseSource(documentId: string) {
1009
1180
  });
1010
1181
  }
1011
1182
 
1183
+ export function applyBuilderAttachCompletion(
1184
+ current: ContentDatabaseResponse | undefined,
1185
+ completion: ContentDatabaseSourceAttachmentAck,
1186
+ ) {
1187
+ if (!current) return current;
1188
+ return {
1189
+ ...current,
1190
+ pagination: current.pagination
1191
+ ? {
1192
+ ...current.pagination,
1193
+ totalItems: completion.importedItemCount,
1194
+ hasMore:
1195
+ current.pagination.returnedItems < completion.importedItemCount,
1196
+ }
1197
+ : current.pagination,
1198
+ attachPreview: {
1199
+ sourceTable: completion.sourceTable,
1200
+ fetchedAt: completion.fetchedAt,
1201
+ importedItemCount: completion.importedItemCount,
1202
+ complete: true,
1203
+ },
1204
+ };
1205
+ }
1206
+
1207
+ export function useBuilderCmsAttachPreview(args: {
1208
+ documentId: string;
1209
+ sourceTable: string | null;
1210
+ fieldPaths?: string[];
1211
+ enabled?: boolean;
1212
+ }) {
1213
+ return useActionQuery<BuilderCmsAttachPreviewResponse>(
1214
+ "preview-content-database-source-attach",
1215
+ args.sourceTable
1216
+ ? {
1217
+ documentId: args.documentId,
1218
+ sourceTable: args.sourceTable,
1219
+ fieldPaths: args.fieldPaths,
1220
+ }
1221
+ : undefined,
1222
+ {
1223
+ enabled: args.enabled !== false && Boolean(args.sourceTable),
1224
+ retry: false,
1225
+ staleTime: 30_000,
1226
+ },
1227
+ );
1228
+ }
1229
+
1230
+ export function writeBuilderAttachPreviewToCache(
1231
+ queryClient: Pick<QueryClient, "setQueriesData">,
1232
+ documentId: string,
1233
+ preview: BuilderCmsAttachPreviewResponse,
1234
+ fallbackData?: ContentDatabaseResponse,
1235
+ ) {
1236
+ queryClient.setQueriesData<ContentDatabaseResponse>(
1237
+ contentDatabaseQueryFilter(documentId),
1238
+ (current) => {
1239
+ const base = current ?? fallbackData ?? preview.base;
1240
+ return base
1241
+ ? {
1242
+ ...base,
1243
+ items: preview.items,
1244
+ pagination: {
1245
+ offset: 0,
1246
+ limit: preview.items.length || 1,
1247
+ totalItems: preview.items.length,
1248
+ returnedItems: preview.items.length,
1249
+ hasMore: preview.hasMore,
1250
+ },
1251
+ attachPreview: {
1252
+ sourceTable: preview.sourceTable,
1253
+ fetchedAt: preview.fetchedAt,
1254
+ },
1255
+ }
1256
+ : current;
1257
+ },
1258
+ );
1259
+ }
1260
+
1012
1261
  export function useChangeContentDatabaseSourceRole(documentId: string) {
1013
1262
  const queryClient = useQueryClient();
1014
1263
  return useActionMutation<
@@ -1032,6 +1281,7 @@ export function useAddContentDatabaseSourceFieldProperty(documentId: string) {
1032
1281
  ContentDatabaseSourceFieldPropertyResponse,
1033
1282
  AddContentDatabaseSourceFieldPropertyRequest
1034
1283
  >("add-content-database-source-field-property", {
1284
+ skipActionQueryInvalidation: true,
1035
1285
  onMutate: async (variables) => {
1036
1286
  await queryClient.cancelQueries({
1037
1287
  queryKey: contentDatabaseQueryKey(documentId),
@@ -1076,6 +1326,9 @@ export function useAddContentDatabaseSourceFieldProperty(documentId: string) {
1076
1326
  queryClient.invalidateQueries({
1077
1327
  queryKey: contentDatabaseQueryKey(documentId),
1078
1328
  });
1329
+ queryClient.invalidateQueries({
1330
+ queryKey: contentDatabaseItemsPageQueryKey,
1331
+ });
1079
1332
  queryClient.invalidateQueries({
1080
1333
  queryKey: ["action", "get-content-database-source", { documentId }],
1081
1334
  });
@@ -1208,6 +1461,7 @@ export function invalidateContentDatabaseSourceRefreshQueries(
1208
1461
  queryClient.invalidateQueries({
1209
1462
  queryKey: contentDatabaseQueryKey(documentId),
1210
1463
  });
1464
+ queryClient.invalidateQueries({ queryKey: contentDatabaseItemsPageQueryKey });
1211
1465
  queryClient.invalidateQueries({
1212
1466
  queryKey: ["action", "get-content-database-source", { documentId }],
1213
1467
  });
@@ -1220,8 +1474,14 @@ export function useProcessBuilderBodyHydration(documentId: string) {
1220
1474
  ProcessBuilderBodyHydrationRequest
1221
1475
  >("process-builder-body-hydration", {
1222
1476
  skipActionQueryInvalidation: true,
1223
- onSuccess: (_data, variables) => {
1224
- invalidateBuilderBodyHydrationQueries(queryClient, documentId, variables);
1477
+ onSuccess: (data, variables) => {
1478
+ if (data.remaining === 0 || variables.documentId) {
1479
+ invalidateBuilderBodyHydrationQueries(
1480
+ queryClient,
1481
+ documentId,
1482
+ variables,
1483
+ );
1484
+ }
1225
1485
  },
1226
1486
  });
1227
1487
  }
@@ -1236,6 +1496,7 @@ export function invalidateBuilderBodyHydrationQueries(
1236
1496
  queryClient.invalidateQueries({
1237
1497
  queryKey: contentDatabaseQueryKey(documentId),
1238
1498
  });
1499
+ queryClient.invalidateQueries({ queryKey: contentDatabaseItemsPageQueryKey });
1239
1500
  queryClient.invalidateQueries({
1240
1501
  queryKey: ["action", "get-content-database-source", { documentId }],
1241
1502
  });
@@ -18,6 +18,7 @@ import {
18
18
  applyDocumentPropertiesToDatabaseResponse,
19
19
  applyDocumentPropertyValueToDatabaseResponse,
20
20
  contentDatabaseQueryFilter,
21
+ contentDatabaseConstrainedQueryFilter,
21
22
  contentDatabaseQueryKey,
22
23
  removeDocumentPropertyFromDatabaseResponse,
23
24
  } from "./use-content-database";
@@ -93,9 +94,9 @@ export function useConfigureDocumentProperty(
93
94
  queryClient.invalidateQueries({
94
95
  queryKey: ["action", "get-document", { id: documentId }],
95
96
  });
96
- queryClient.invalidateQueries({
97
- ...contentDatabaseQueryFilter(databaseDocumentId),
98
- });
97
+ queryClient.invalidateQueries(
98
+ contentDatabaseConstrainedQueryFilter(databaseDocumentId),
99
+ );
99
100
  },
100
101
  });
101
102
  return withDatabaseScope(mutation, databaseId);
@@ -160,9 +161,9 @@ export function useSetDocumentProperty(
160
161
  queryClient.invalidateQueries({
161
162
  queryKey: ["action", "get-document", { id: variables.documentId }],
162
163
  });
163
- queryClient.invalidateQueries({
164
- ...contentDatabaseQueryFilter(databaseDocumentId),
165
- });
164
+ queryClient.invalidateQueries(
165
+ contentDatabaseConstrainedQueryFilter(databaseDocumentId),
166
+ );
166
167
  queryClient.invalidateQueries({
167
168
  queryKey: [
168
169
  "action",
@@ -26,6 +26,7 @@ const databaseMessages = {
26
26
  addingDetailsMatchedOn: "Adding details matched on {{field}}.",
27
27
  addingItems: "Adding items",
28
28
  addingItemsAsRows: "Adding items as their own rows in this database.",
29
+ attachingReadOnly: "Attaching · read-only",
29
30
  addMoreItemsToThisList: "Add more items to this list",
30
31
  addProperty: "Add property",
31
32
  allAvailableDetailFieldsAlreadyVisible:
@@ -8962,6 +8963,7 @@ export const messagesByLocale = {
8962
8963
  builderRowsFetchedSoFar: "目前已获取 {{count}} 行。",
8963
8964
  builderRowsFinishingUp: "Builder 行即将完成加载。",
8964
8965
  builderRowsLoadingBackground: "Builder 仍在后台加载行。",
8966
+ attachingReadOnly: "正在附加 · 只读",
8965
8967
  builderRowsLoadingHitSnag: "Builder 行加载遇到问题。",
8966
8968
  opening: "正在打开...",
8967
8969
  },
@@ -9147,6 +9149,7 @@ export const messagesByLocale = {
9147
9149
  "Las filas de Builder están terminando de cargarse.",
9148
9150
  builderRowsLoadingBackground:
9149
9151
  "Builder sigue cargando filas en segundo plano.",
9152
+ attachingReadOnly: "Adjuntando · solo lectura",
9150
9153
  builderRowsLoadingHitSnag:
9151
9154
  "La carga de filas de Builder tuvo un problema.",
9152
9155
  opening: "Abriendo...",
@@ -9344,6 +9347,7 @@ export const messagesByLocale = {
9344
9347
  builderRowsFinishingUp: "Les lignes Builder terminent leur chargement.",
9345
9348
  builderRowsLoadingBackground:
9346
9349
  "Builder charge encore des lignes en arrière-plan.",
9350
+ attachingReadOnly: "Ajout en cours · lecture seule",
9347
9351
  builderRowsLoadingHitSnag:
9348
9352
  "Le chargement des lignes Builder a rencontré un problème.",
9349
9353
  opening: "Ouverture...",
@@ -9540,6 +9544,7 @@ export const messagesByLocale = {
9540
9544
  builderRowsFinishingUp: "Builder-Zeilen werden fertig geladen.",
9541
9545
  builderRowsLoadingBackground:
9542
9546
  "Builder lädt weiterhin Zeilen im Hintergrund.",
9547
+ attachingReadOnly: "Wird angehängt · schreibgeschützt",
9543
9548
  builderRowsLoadingHitSnag:
9544
9549
  "Beim Laden der Builder-Zeilen ist ein Problem aufgetreten.",
9545
9550
  opening: "Wird geöffnet...",
@@ -9736,6 +9741,7 @@ export const messagesByLocale = {
9736
9741
  builderRowsFinishingUp: "Builder 行の読み込みを完了しています。",
9737
9742
  builderRowsLoadingBackground:
9738
9743
  "Builder はバックグラウンドで行を読み込み続けています。",
9744
+ attachingReadOnly: "接続中 · 読み取り専用",
9739
9745
  builderRowsLoadingHitSnag: "Builder 行の読み込みで問題が発生しました。",
9740
9746
  opening: "開いています...",
9741
9747
  },
@@ -9928,6 +9934,7 @@ export const messagesByLocale = {
9928
9934
  builderRowsFinishingUp: "Builder 행 로드를 마무리하는 중입니다.",
9929
9935
  builderRowsLoadingBackground:
9930
9936
  "Builder가 백그라운드에서 행을 계속 로드하고 있습니다.",
9937
+ attachingReadOnly: "연결 중 · 읽기 전용",
9931
9938
  builderRowsLoadingHitSnag: "Builder 행 로드 중 문제가 발생했습니다.",
9932
9939
  opening: "여는 중...",
9933
9940
  },
@@ -10115,6 +10122,7 @@ export const messagesByLocale = {
10115
10122
  "As linhas do Builder estão terminando de carregar.",
10116
10123
  builderRowsLoadingBackground:
10117
10124
  "O Builder ainda está carregando linhas em segundo plano.",
10125
+ attachingReadOnly: "Anexando · somente leitura",
10118
10126
  builderRowsLoadingHitSnag:
10119
10127
  "O carregamento de linhas do Builder encontrou um problema.",
10120
10128
  opening: "Abrindo...",
@@ -10307,6 +10315,7 @@ export const messagesByLocale = {
10307
10315
  builderRowsFinishingUp: "Builder पंक्तियां लोड होना पूरा कर रही हैं।",
10308
10316
  builderRowsLoadingBackground:
10309
10317
  "Builder अभी भी पृष्ठभूमि में पंक्तियां लोड कर रहा है।",
10318
+ attachingReadOnly: "संलग्न किया जा रहा है · केवल पढ़ने के लिए",
10310
10319
  builderRowsLoadingHitSnag: "Builder पंक्तियां लोड करने में समस्या आई।",
10311
10320
  opening: "खोला जा रहा है...",
10312
10321
  },
@@ -10488,6 +10497,7 @@ export const messagesByLocale = {
10488
10497
  builderRowsFetchedSoFar: "تم جلب {{count}} صفًا حتى الآن.",
10489
10498
  builderRowsFinishingUp: "صفوف Builder توشك على إكمال التحميل.",
10490
10499
  builderRowsLoadingBackground: "لا يزال Builder يحمّل الصفوف في الخلفية.",
10500
+ attachingReadOnly: "جارٍ الإرفاق · للقراءة فقط",
10491
10501
  builderRowsLoadingHitSnag: "واجه تحميل صفوف Builder مشكلة.",
10492
10502
  opening: "جارٍ الفتح...",
10493
10503
  },
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-29
4
+ ---
5
+
6
+ Large databases keep useful rows visible while table sorting and Builder review details load.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Builder source columns now appear immediately when connected entries already contain the selected field, including empty values.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-30
4
+ ---
5
+
6
+ Large Builder-backed tables show useful rows sooner and finish loading in the background.