@agent-native/core 0.135.0 → 0.135.2

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 (50) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/getting-started-actions.mdx +235 -0
  4. package/corpus/core/docs/content/getting-started-database.mdx +253 -0
  5. package/corpus/core/docs/content/getting-started-pages.mdx +190 -0
  6. package/corpus/core/docs/content/getting-started.mdx +57 -613
  7. package/corpus/core/package.json +1 -1
  8. package/corpus/core/src/sharing/access.ts +44 -2
  9. package/corpus/templates/clips/actions/add-comment.ts +6 -2
  10. package/corpus/templates/clips/app/components/player/comments-panel.tsx +9 -2
  11. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +44 -27
  12. package/corpus/templates/clips/app/components/player/scrubber.tsx +10 -2
  13. package/corpus/templates/clips/app/components/player/video-player.tsx +4 -0
  14. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +22 -15
  15. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -0
  16. package/corpus/templates/content/actions/_database-utils.ts +113 -4
  17. package/corpus/templates/content/actions/get-document.ts +84 -3
  18. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +9 -2
  19. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +29 -13
  20. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +5 -12
  21. package/corpus/templates/content/app/components/editor/body-hydration.ts +12 -6
  22. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +2 -3
  23. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -12
  24. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -27
  25. package/corpus/templates/content/app/hooks/use-create-page.ts +3 -6
  26. package/corpus/templates/content/app/hooks/use-document-properties.ts +9 -16
  27. package/corpus/templates/content/app/hooks/use-document-versions.ts +3 -3
  28. package/corpus/templates/content/app/hooks/use-documents.ts +85 -29
  29. package/corpus/templates/content/app/hooks/use-notion.ts +15 -13
  30. package/corpus/templates/content/app/i18n-data.ts +32 -1
  31. package/corpus/templates/content/app/lib/document-query.ts +36 -0
  32. package/corpus/templates/content/changelog/2026-08-02-pages-opened-from-a-database-now-keep-that-database-s-fields.md +6 -0
  33. package/corpus/templates/content/server/lib/document-context.ts +11 -6
  34. package/corpus/templates/content/shared/api.ts +8 -0
  35. package/dist/collab/struct-routes.d.ts +1 -1
  36. package/dist/notifications/routes.d.ts +3 -3
  37. package/dist/observability/routes.d.ts +1 -1
  38. package/dist/progress/routes.d.ts +1 -1
  39. package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
  40. package/dist/provider-api/actions/provider-api.d.ts +4 -4
  41. package/dist/secrets/routes.d.ts +9 -9
  42. package/dist/sharing/access.d.ts.map +1 -1
  43. package/dist/sharing/access.js +32 -2
  44. package/dist/sharing/access.js.map +1 -1
  45. package/docs/content/getting-started-actions.mdx +235 -0
  46. package/docs/content/getting-started-database.mdx +253 -0
  47. package/docs/content/getting-started-pages.mdx +190 -0
  48. package/docs/content/getting-started.mdx +57 -613
  49. package/package.json +1 -1
  50. package/src/sharing/access.ts +44 -2
@@ -62,8 +62,10 @@ import {
62
62
  type ContentSpaceSummary,
63
63
  } from "@/hooks/use-content-spaces";
64
64
  import {
65
+ mergeDocumentIntoDocumentCache,
65
66
  isDocumentUpdateConflict,
66
67
  patchDocumentCaches,
68
+ documentQueryFilter,
67
69
  useDocument,
68
70
  useDeleteDocument,
69
71
  useDocuments,
@@ -247,7 +249,10 @@ export function DocumentEditor({
247
249
  databaseId,
248
250
  databaseDocumentId,
249
251
  }: DocumentEditorProps) {
250
- const documentQuery = useDocument(documentId);
252
+ const documentQuery = useDocument(documentId, {
253
+ databaseId,
254
+ databaseDocumentId,
255
+ });
251
256
  const {
252
257
  data: queriedDocument,
253
258
  isError,
@@ -539,7 +544,7 @@ function DocumentEditorBody({
539
544
  const deleteDocument = useDeleteDocument();
540
545
  const queryClient = useQueryClient();
541
546
  const processBuilderBodies = useProcessBuilderBodyHydration(
542
- document.databaseMembership?.databaseDocumentId ?? documentId,
547
+ document.bodyHydration?.databaseDocumentId ?? documentId,
543
548
  );
544
549
  const canEdit = document.canEdit ?? true;
545
550
  const canEditRef = useRef(canEdit);
@@ -664,24 +669,30 @@ function DocumentEditorBody({
664
669
  );
665
670
 
666
671
  useEffect(() => {
667
- const membership = document.databaseMembership;
668
- const hydration = membership?.bodyHydration;
672
+ const hydrationContext = document.bodyHydration;
673
+ const hydration = hydrationContext?.hydration;
669
674
  if (
670
- !membership?.sourceId ||
675
+ !canEdit ||
676
+ !hydrationContext?.sourceId ||
671
677
  !hydration ||
672
678
  (hydration.status !== "pending" && hydration.status !== "error")
673
679
  ) {
674
680
  return;
675
681
  }
676
- const promotionKey = `${membership.sourceId}:${documentId}:${hydration.status}:${hydration.version ?? ""}`;
682
+ const promotionKey = `${hydrationContext.sourceId}:${documentId}:${hydration.status}:${hydration.version ?? ""}`;
677
683
  if (promotedBuilderBodyRef.current === promotionKey) return;
678
684
  promotedBuilderBodyRef.current = promotionKey;
679
685
  processBuilderBodies.mutate({
680
- sourceId: membership.sourceId,
686
+ sourceId: hydrationContext.sourceId,
681
687
  documentId,
682
688
  limit: 1,
683
689
  });
684
- }, [document.databaseMembership, documentId, processBuilderBodies.mutate]);
690
+ }, [
691
+ canEdit,
692
+ document.bodyHydration,
693
+ documentId,
694
+ processBuilderBodies.mutate,
695
+ ]);
685
696
  const titleFocusedRef = useRef(false);
686
697
  const scrollContainerRef = useRef<HTMLDivElement>(null);
687
698
  const titleInputRef = useRef<HTMLTextAreaElement>(null);
@@ -976,9 +987,8 @@ function DocumentEditorBody({
976
987
  description:
977
988
  error instanceof Error ? error.message : t("empty.genericError"),
978
989
  });
979
- queryClient.setQueryData(
980
- ["action", "get-document", { id: documentId }],
981
- fileFirstDocument,
990
+ queryClient.setQueriesData(documentQueryFilter(documentId), (old) =>
991
+ mergeDocumentIntoDocumentCache(old, fileFirstDocument),
982
992
  );
983
993
  queryClient.invalidateQueries({
984
994
  queryKey: ["action", "list-documents"],
@@ -1890,9 +1900,15 @@ function DocumentEditorBody({
1890
1900
  if (bodyHydrationPending) {
1891
1901
  return (
1892
1902
  <BuilderBodySyncingNotice
1893
- title={t("editor.builderBodySyncing")}
1903
+ title={t(
1904
+ document.bodyHydration?.provider === "builder"
1905
+ ? "editor.builderBodySyncing"
1906
+ : "editor.pageBodySyncing",
1907
+ )}
1894
1908
  description={t(
1895
- "editor.builderBodySyncingDescription",
1909
+ document.bodyHydration?.provider === "builder"
1910
+ ? "editor.builderBodySyncingDescription"
1911
+ : "editor.pageBodySyncingDescription",
1896
1912
  )}
1897
1913
  />
1898
1914
  );
@@ -90,6 +90,7 @@ import {
90
90
  useSearchNotionPages,
91
91
  useCreateAndLinkNotionPage,
92
92
  } from "@/hooks/use-notion";
93
+ import { documentQueryFilter } from "@/lib/document-query";
93
94
  import {
94
95
  localSourceAbsolutePath,
95
96
  revealLinkedLocalSourceFile,
@@ -572,12 +573,8 @@ export function DocumentToolbar({
572
573
  const previous = hideFromSearch;
573
574
  setPendingHideFromSearch(next);
574
575
 
575
- queryClient.setQueryData(
576
- ["action", "get-document", { id: documentId }],
577
- (old: any) =>
578
- old && typeof old === "object"
579
- ? { ...old, hideFromSearch: next }
580
- : old,
576
+ queryClient.setQueriesData(documentQueryFilter(documentId), (old: any) =>
577
+ old && typeof old === "object" ? { ...old, hideFromSearch: next } : old,
581
578
  );
582
579
  queryClient.setQueryData(
583
580
  ["action", "list-documents", undefined],
@@ -601,9 +598,7 @@ export function DocumentToolbar({
601
598
  });
602
599
  } catch (err) {
603
600
  setPendingHideFromSearch(previous);
604
- queryClient.invalidateQueries({
605
- queryKey: ["action", "get-document", { id: documentId }],
606
- });
601
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
607
602
  queryClient.invalidateQueries({
608
603
  queryKey: ["action", "list-documents"],
609
604
  });
@@ -614,9 +609,7 @@ export function DocumentToolbar({
614
609
  throw err;
615
610
  } finally {
616
611
  setPendingHideFromSearch(null);
617
- queryClient.invalidateQueries({
618
- queryKey: ["action", "get-document", { id: documentId }],
619
- });
612
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
620
613
  queryClient.invalidateQueries({
621
614
  queryKey: ["action", "list-documents"],
622
615
  });
@@ -54,12 +54,12 @@ export function databaseItemBodyHydrationIsPending(
54
54
  }
55
55
 
56
56
  export function documentBodyHydrationIsPending(
57
- document: Pick<Document, "content" | "databaseMembership">,
57
+ document: Pick<Document, "content" | "bodyHydration">,
58
58
  ) {
59
- const hydration = document.databaseMembership?.bodyHydration;
59
+ const hydration = document.bodyHydration?.hydration;
60
60
  if (
61
61
  sourceBackedEmptyBodyNeedsHydration({
62
- sourceId: document.databaseMembership?.sourceId,
62
+ sourceId: document.bodyHydration ? "source-backed" : undefined,
63
63
  content: document.content,
64
64
  hydration,
65
65
  })
@@ -81,7 +81,10 @@ export function newDocumentPageChoiceIsDisabled(args: {
81
81
 
82
82
  export function previewBodyHydrationIsPending(args: {
83
83
  item: Pick<ContentDatabaseItem, "bodyHydration" | "document">;
84
- document: Pick<Document, "content" | "databaseMembership"> | null | undefined;
84
+ document:
85
+ | Pick<Document, "content" | "databaseMembership" | "bodyHydration">
86
+ | null
87
+ | undefined;
85
88
  }) {
86
89
  const membership =
87
90
  args.document?.databaseMembership ?? args.item.document.databaseMembership;
@@ -128,11 +131,14 @@ export function previewDraftConflictsWithHydratedBody(args: {
128
131
 
129
132
  export function previewBodyHydrationIsTerminalError(args: {
130
133
  item: Pick<ContentDatabaseItem, "bodyHydration" | "document">;
131
- document: Pick<Document, "databaseMembership"> | null | undefined;
134
+ document:
135
+ | Pick<Document, "databaseMembership" | "bodyHydration">
136
+ | null
137
+ | undefined;
132
138
  }) {
133
139
  return (
134
140
  builderBodyHydrationIsTerminalError(
135
- args.document?.databaseMembership?.bodyHydration,
141
+ args.document?.bodyHydration?.hydration,
136
142
  ) ||
137
143
  builderBodyHydrationIsTerminalError(
138
144
  args.item.bodyHydration ??
@@ -204,6 +204,7 @@ import {
204
204
  } from "@/hooks/use-document-properties";
205
205
  import {
206
206
  isDocumentUpdateConflict,
207
+ documentQueryFilter,
207
208
  type DocumentUpdateResult,
208
209
  useDeleteDocument,
209
210
  useDocument,
@@ -4853,9 +4854,7 @@ function DatabaseItemPreview({
4853
4854
  void queryClient.invalidateQueries({
4854
4855
  queryKey: contentDatabaseQueryKey(databaseDocumentId),
4855
4856
  });
4856
- void queryClient.invalidateQueries({
4857
- queryKey: ["action", "get-document", { id: document.id }],
4858
- });
4857
+ void queryClient.invalidateQueries(documentQueryFilter(document.id));
4859
4858
  void queryClient.invalidateQueries({
4860
4859
  queryKey: ["action", "list-documents"],
4861
4860
  });
@@ -126,6 +126,7 @@ import {
126
126
  useUpdateDocument,
127
127
  buildDocumentTree,
128
128
  filterDocumentTreeDocuments,
129
+ documentQueryFilter,
129
130
  } from "@/hooks/use-documents";
130
131
  import { useLocalStorage } from "@/hooks/use-local-storage";
131
132
  import {
@@ -1191,16 +1192,12 @@ export function DocumentSidebar({
1191
1192
  created,
1192
1193
  );
1193
1194
  if (nextId !== id) {
1194
- queryClient.removeQueries({
1195
- queryKey: ["action", "get-document", { id }],
1196
- });
1195
+ queryClient.removeQueries(documentQueryFilter(id));
1197
1196
  navigateToDocument(nextId);
1198
1197
  }
1199
1198
  // Replace optimistic doc with real server doc + clear any 404 error
1200
1199
  // state from the in-flight fetch that ran before create completed.
1201
- queryClient.invalidateQueries({
1202
- queryKey: ["action", "get-document", { id: nextId }],
1203
- });
1200
+ queryClient.invalidateQueries(documentQueryFilter(nextId));
1204
1201
  queryClient.invalidateQueries({
1205
1202
  queryKey: ["action", "list-documents"],
1206
1203
  });
@@ -1214,9 +1211,7 @@ export function DocumentSidebar({
1214
1211
  queryClient.invalidateQueries({
1215
1212
  queryKey: ["action", "list-documents"],
1216
1213
  });
1217
- queryClient.removeQueries({
1218
- queryKey: ["action", "get-document", { id }],
1219
- });
1214
+ queryClient.removeQueries(documentQueryFilter(id));
1220
1215
  if (rootFilesDatabaseId) {
1221
1216
  queryClient.setQueryData<ContentDatabaseResponse>(
1222
1217
  contentDatabaseByIdQueryKey(rootFilesDatabaseId),
@@ -1300,9 +1295,7 @@ export function DocumentSidebar({
1300
1295
  );
1301
1296
  });
1302
1297
  for (const deletedId of deletedIds) {
1303
- queryClient.removeQueries({
1304
- queryKey: ["action", "get-document", { id: deletedId }],
1305
- });
1298
+ queryClient.removeQueries(documentQueryFilter(deletedId));
1306
1299
  }
1307
1300
 
1308
1301
  if (activeDeleted) {
@@ -57,6 +57,8 @@ import type {
57
57
  import type { Query, QueryClient } from "@tanstack/react-query";
58
58
  import { useQueryClient } from "@tanstack/react-query";
59
59
 
60
+ import { documentQueryFilter } from "../lib/document-query";
61
+
60
62
  export function contentDatabaseQueryKey(documentId: string) {
61
63
  return ["action", "get-content-database", { documentId }] as const;
62
64
  }
@@ -421,9 +423,7 @@ export function clearDeletedContentDatabaseFromCache(
421
423
  documentId: string,
422
424
  ) {
423
425
  queryClient.removeQueries(contentDatabaseQueryFilter(documentId));
424
- queryClient.removeQueries({
425
- queryKey: ["action", "get-document", { id: documentId }],
426
- });
426
+ queryClient.removeQueries(documentQueryFilter(documentId));
427
427
  queryClient.invalidateQueries({
428
428
  queryKey: ["action", "get-content-database"],
429
429
  });
@@ -713,20 +713,14 @@ export function useCreateContentDatabase(documentId: string | null) {
713
713
  {
714
714
  onSuccess: (data) => {
715
715
  if (documentId) {
716
- queryClient.invalidateQueries({
717
- queryKey: ["action", "get-document", { id: documentId }],
718
- });
716
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
719
717
  queryClient.invalidateQueries({
720
718
  queryKey: contentDatabaseQueryKey(documentId),
721
719
  });
722
720
  }
723
- queryClient.invalidateQueries({
724
- queryKey: [
725
- "action",
726
- "get-document",
727
- { id: data.database.documentId },
728
- ],
729
- });
721
+ queryClient.invalidateQueries(
722
+ documentQueryFilter(data.database.documentId),
723
+ );
730
724
  queryClient.invalidateQueries({
731
725
  queryKey: ["action", "list-documents"],
732
726
  });
@@ -743,17 +737,11 @@ export function useCreateInlineContentDatabase(hostDocumentId: string | null) {
743
737
  >("create-inline-content-database", {
744
738
  onSuccess: (data) => {
745
739
  if (hostDocumentId) {
746
- queryClient.invalidateQueries({
747
- queryKey: ["action", "get-document", { id: hostDocumentId }],
748
- });
740
+ queryClient.invalidateQueries(documentQueryFilter(hostDocumentId));
749
741
  }
750
- queryClient.invalidateQueries({
751
- queryKey: [
752
- "action",
753
- "get-document",
754
- { id: data.block.databaseDocumentId },
755
- ],
756
- });
742
+ queryClient.invalidateQueries(
743
+ documentQueryFilter(data.block.databaseDocumentId),
744
+ );
757
745
  queryClient.invalidateQueries({
758
746
  queryKey: contentDatabaseQueryKey(data.block.databaseDocumentId),
759
747
  });
@@ -1488,7 +1476,10 @@ export function useProcessBuilderBodyHydration(documentId: string) {
1488
1476
 
1489
1477
  export function invalidateBuilderBodyHydrationQueries(
1490
1478
  queryClient: {
1491
- invalidateQueries: (filters: { queryKey: readonly unknown[] }) => unknown;
1479
+ invalidateQueries: (filters: {
1480
+ queryKey?: readonly unknown[];
1481
+ predicate?: (query: { queryKey: readonly unknown[] }) => boolean;
1482
+ }) => unknown;
1492
1483
  },
1493
1484
  documentId: string,
1494
1485
  variables?: Pick<ProcessBuilderBodyHydrationRequest, "documentId"> | null,
@@ -1501,9 +1492,7 @@ export function invalidateBuilderBodyHydrationQueries(
1501
1492
  queryKey: ["action", "get-content-database-source", { documentId }],
1502
1493
  });
1503
1494
  if (variables?.documentId) {
1504
- queryClient.invalidateQueries({
1505
- queryKey: ["action", "get-document", { id: variables.documentId }],
1506
- });
1495
+ queryClient.invalidateQueries(documentQueryFilter(variables.documentId));
1507
1496
  }
1508
1497
  }
1509
1498
 
@@ -12,6 +12,7 @@ import {
12
12
  import { useContentSpaces } from "@/hooks/use-content-spaces";
13
13
  import { useCreateDocument } from "@/hooks/use-documents";
14
14
  import { useLocalStorage } from "@/hooks/use-local-storage";
15
+ import { documentQueryFilter } from "@/lib/document-query";
15
16
  import { markDocumentCreationPending } from "@/lib/optimistic-document";
16
17
 
17
18
  const LIST_DOCUMENTS_QUERY_KEY = [
@@ -103,9 +104,7 @@ export function useCreatePage(opts?: {
103
104
  );
104
105
  // Replace optimistic doc with real server doc + clear any 404 error
105
106
  // state from the in-flight fetch that ran before create completed.
106
- queryClient.invalidateQueries({
107
- queryKey: ["action", "get-document", { id }],
108
- });
107
+ queryClient.invalidateQueries(documentQueryFilter(id));
109
108
  queryClient.invalidateQueries({
110
109
  queryKey: ["action", "list-documents"],
111
110
  });
@@ -115,9 +114,7 @@ export function useCreatePage(opts?: {
115
114
  queryClient.invalidateQueries({
116
115
  queryKey: ["action", "list-documents"],
117
116
  });
118
- queryClient.removeQueries({
119
- queryKey: ["action", "get-document", { id }],
120
- });
117
+ queryClient.removeQueries(documentQueryFilter(id));
121
118
  if (shouldNavigate) navigate("/");
122
119
  toast.error("Failed to create page", {
123
120
  description:
@@ -22,7 +22,10 @@ import {
22
22
  contentDatabaseQueryKey,
23
23
  removeDocumentPropertyFromDatabaseResponse,
24
24
  } from "./use-content-database";
25
- import { documentPropertiesQueryKey } from "./use-documents";
25
+ import {
26
+ documentPropertiesQueryKey,
27
+ documentQueryFilter,
28
+ } from "./use-documents";
26
29
 
27
30
  type DatabaseScopedRequest = { databaseId: string };
28
31
 
@@ -91,9 +94,7 @@ export function useConfigureDocumentProperty(
91
94
  queryClient.invalidateQueries({
92
95
  queryKey: documentPropertiesQueryKey(documentId, databaseId),
93
96
  });
94
- queryClient.invalidateQueries({
95
- queryKey: ["action", "get-document", { id: documentId }],
96
- });
97
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
97
98
  queryClient.invalidateQueries(
98
99
  contentDatabaseConstrainedQueryFilter(databaseDocumentId),
99
100
  );
@@ -158,9 +159,7 @@ export function useSetDocumentProperty(
158
159
  queryClient.invalidateQueries({
159
160
  queryKey: documentPropertiesQueryKey(variables.documentId, databaseId),
160
161
  });
161
- queryClient.invalidateQueries({
162
- queryKey: ["action", "get-document", { id: variables.documentId }],
163
- });
162
+ queryClient.invalidateQueries(documentQueryFilter(variables.documentId));
164
163
  queryClient.invalidateQueries(
165
164
  contentDatabaseConstrainedQueryFilter(databaseDocumentId),
166
165
  );
@@ -195,9 +194,7 @@ export function useDuplicateDocumentProperty(
195
194
  queryClient.invalidateQueries({
196
195
  queryKey: documentPropertiesQueryKey(documentId, databaseId),
197
196
  });
198
- queryClient.invalidateQueries({
199
- queryKey: ["action", "get-document", { id: documentId }],
200
- });
197
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
201
198
  queryClient.invalidateQueries({
202
199
  ...contentDatabaseQueryFilter(databaseDocumentId),
203
200
  });
@@ -220,9 +217,7 @@ export function useReorderDocumentProperty(
220
217
  queryClient.invalidateQueries({
221
218
  queryKey: documentPropertiesQueryKey(documentId, databaseId),
222
219
  });
223
- queryClient.invalidateQueries({
224
- queryKey: ["action", "get-document", { id: documentId }],
225
- });
220
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
226
221
  queryClient.invalidateQueries({
227
222
  queryKey: contentDatabaseQueryKey(databaseDocumentId),
228
223
  });
@@ -277,9 +272,7 @@ export function useDeleteDocumentProperty(
277
272
  queryClient.invalidateQueries({
278
273
  queryKey: documentPropertiesQueryKey(documentId, databaseId),
279
274
  });
280
- queryClient.invalidateQueries({
281
- queryKey: ["action", "get-document", { id: documentId }],
282
- });
275
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
283
276
  queryClient.invalidateQueries({
284
277
  ...contentDatabaseQueryFilter(databaseDocumentId),
285
278
  });
@@ -5,6 +5,8 @@ import {
5
5
  import type { Document, DocumentVersion } from "@shared/api";
6
6
  import { useQueryClient } from "@tanstack/react-query";
7
7
 
8
+ import { documentQueryFilter } from "./use-documents";
9
+
8
10
  export function useDocumentVersions(documentId: string | null) {
9
11
  return useActionQuery<DocumentVersion[]>(
10
12
  "list-document-versions",
@@ -29,9 +31,7 @@ export function useRestoreDocumentVersion(documentId: string) {
29
31
  queryClient.invalidateQueries({
30
32
  queryKey: ["action", "list-document-versions", { documentId }],
31
33
  });
32
- queryClient.invalidateQueries({
33
- queryKey: ["action", "get-document", { id: documentId }],
34
- });
34
+ queryClient.invalidateQueries(documentQueryFilter(documentId));
35
35
  },
36
36
  },
37
37
  );
@@ -19,23 +19,50 @@ import { useQueryClient } from "@tanstack/react-query";
19
19
  import { toast } from "sonner";
20
20
 
21
21
  import type { DocumentUpdateConflictResponse } from "../../actions/update-document";
22
+ import {
23
+ documentQueryFilter,
24
+ documentQueryKey,
25
+ type DocumentQueryContext,
26
+ } from "../lib/document-query";
22
27
  import {
23
28
  removeOptimisticItemFromContentDatabase,
24
29
  useRestoreContentDatabase,
25
30
  } from "./use-content-database";
26
31
 
32
+ export {
33
+ documentQueryFilter,
34
+ documentQueryKey,
35
+ type DocumentQueryContext,
36
+ } from "../lib/document-query";
37
+
27
38
  export type { DocumentUpdateConflictResponse };
28
39
 
40
+ export type PageOwnedDocumentCachePatch = Pick<
41
+ Partial<Document>,
42
+ | "id"
43
+ | "parentId"
44
+ | "title"
45
+ | "content"
46
+ | "description"
47
+ | "icon"
48
+ | "position"
49
+ | "isFavorite"
50
+ | "hideFromSearch"
51
+ | "visibility"
52
+ | "accessRole"
53
+ | "canEdit"
54
+ | "canManage"
55
+ | "source"
56
+ | "createdAt"
57
+ | "updatedAt"
58
+ >;
59
+
29
60
  export const LIST_DOCUMENTS_QUERY_KEY = [
30
61
  "action",
31
62
  "list-documents",
32
63
  undefined,
33
64
  ] as const;
34
65
 
35
- export function documentQueryKey(documentId: string) {
36
- return ["action", "get-document", { id: documentId }] as const;
37
- }
38
-
39
66
  export function documentPropertiesQueryKey(
40
67
  documentId: string,
41
68
  databaseId: string,
@@ -74,7 +101,27 @@ export function mergeDocumentIntoDocumentCache(
74
101
  old: unknown,
75
102
  document: Document,
76
103
  ) {
77
- return old && typeof old === "object" ? { ...old, ...document } : document;
104
+ const pageOwnedPatch: PageOwnedDocumentCachePatch = {
105
+ id: document.id,
106
+ parentId: document.parentId,
107
+ title: document.title,
108
+ content: document.content,
109
+ description: document.description,
110
+ icon: document.icon,
111
+ position: document.position,
112
+ isFavorite: document.isFavorite,
113
+ hideFromSearch: document.hideFromSearch,
114
+ visibility: document.visibility,
115
+ accessRole: document.accessRole,
116
+ canEdit: document.canEdit,
117
+ canManage: document.canManage,
118
+ source: document.source,
119
+ createdAt: document.createdAt,
120
+ updatedAt: document.updatedAt,
121
+ };
122
+ return old && typeof old === "object"
123
+ ? { ...old, ...pageOwnedPatch }
124
+ : pageOwnedPatch;
78
125
  }
79
126
 
80
127
  export function mergeDocumentIntoListDocumentsCache(
@@ -157,9 +204,9 @@ function patchDocumentWithFavoriteMembershipInDatabaseCache(
157
204
  export function patchDocumentCaches(
158
205
  queryClient: Pick<QueryClient, "setQueryData" | "setQueriesData">,
159
206
  documentId: string,
160
- patch: Partial<Document>,
207
+ patch: PageOwnedDocumentCachePatch,
161
208
  ) {
162
- queryClient.setQueryData(documentQueryKey(documentId), (old: unknown) =>
209
+ queryClient.setQueriesData(documentQueryFilter(documentId), (old: unknown) =>
163
210
  old && typeof old === "object" ? { ...old, ...patch } : old,
164
211
  );
165
212
  queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, (old: unknown) =>
@@ -219,7 +266,7 @@ export function patchContentSpaceNameCaches(
219
266
  export function documentUpdateSuccessPatch(
220
267
  data: DocumentUpdateResponse,
221
268
  variables: DocumentUpdateRequestWithCas,
222
- ): Partial<Document> {
269
+ ): PageOwnedDocumentCachePatch {
223
270
  return {
224
271
  updatedAt: data.updatedAt,
225
272
  ...(variables.title !== undefined ? { title: data.title } : {}),
@@ -289,13 +336,28 @@ export const DOCUMENT_QUERY_FRESHNESS_OPTIONS = {
289
336
  retry: false,
290
337
  };
291
338
 
292
- export function useDocument(id: string | null) {
293
- return useActionQuery<Document>("get-document", id ? { id } : undefined, {
294
- enabled: !!id,
295
- // Doc-not-found / no-access errors are deterministic — retrying just keeps
296
- // the spinner up for ~7s before the UI can render "Not found".
297
- ...DOCUMENT_QUERY_FRESHNESS_OPTIONS,
298
- });
339
+ export function useDocument(
340
+ id: string | null,
341
+ context: DocumentQueryContext = {},
342
+ ) {
343
+ return useActionQuery<Document>(
344
+ "get-document",
345
+ id
346
+ ? {
347
+ id,
348
+ ...(context.databaseId ? { databaseId: context.databaseId } : {}),
349
+ ...(context.databaseDocumentId
350
+ ? { databaseDocumentId: context.databaseDocumentId }
351
+ : {}),
352
+ }
353
+ : undefined,
354
+ {
355
+ enabled: !!id,
356
+ // Doc-not-found / no-access errors are deterministic — retrying just keeps
357
+ // the spinner up for ~7s before the UI can render "Not found".
358
+ ...DOCUMENT_QUERY_FRESHNESS_OPTIONS,
359
+ },
360
+ );
299
361
  }
300
362
 
301
363
  export interface PreviewDocumentDraftRecord {
@@ -368,7 +430,7 @@ export function useUpdateDocument() {
368
430
  };
369
431
  if (Object.keys(optimisticPatch).length === 0) return undefined;
370
432
 
371
- const documentKey = documentQueryKey(variables.id);
433
+ const documentFilter = documentQueryFilter(variables.id);
372
434
  const databaseFilter = {
373
435
  queryKey: ["action", "get-content-database"],
374
436
  } as const;
@@ -376,14 +438,14 @@ export function useUpdateDocument() {
376
438
  queryKey: ["action", "list-content-spaces"],
377
439
  } as const;
378
440
  await Promise.all([
379
- queryClient.cancelQueries({ queryKey: documentKey }),
441
+ queryClient.cancelQueries(documentFilter),
380
442
  queryClient.cancelQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY }),
381
443
  queryClient.cancelQueries(databaseFilter),
382
444
  queryClient.cancelQueries(contentSpacesFilter),
383
445
  ]);
384
446
 
385
447
  const previous: Array<[readonly unknown[], unknown]> = [
386
- [documentKey, queryClient.getQueryData(documentKey)],
448
+ ...queryClient.getQueriesData(documentFilter),
387
449
  [
388
450
  LIST_DOCUMENTS_QUERY_KEY,
389
451
  queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY),
@@ -423,8 +485,8 @@ export function useUpdateDocument() {
423
485
  // just-applied write.
424
486
  if (isDocumentUpdateConflict(data)) {
425
487
  const serverDocument = data.document;
426
- queryClient.setQueryData(
427
- ["action", "get-document", { id: variables.id }],
488
+ queryClient.setQueriesData(
489
+ documentQueryFilter(variables.id),
428
490
  (old: unknown) =>
429
491
  mergeDocumentIntoDocumentCache(old, serverDocument),
430
492
  );
@@ -453,9 +515,7 @@ export function useUpdateDocument() {
453
515
  queryKey: ["action", "get-content-database"],
454
516
  });
455
517
  }
456
- queryClient.invalidateQueries({
457
- queryKey: ["action", "get-document", { id: variables.id }],
458
- });
518
+ queryClient.invalidateQueries(documentQueryFilter(variables.id));
459
519
  queryClient.invalidateQueries({
460
520
  queryKey: ["action", "list-documents"],
461
521
  });
@@ -525,9 +585,7 @@ export function useDeleteDocument() {
525
585
  queryClient.invalidateQueries({
526
586
  queryKey: ["action", "list-documents"],
527
587
  });
528
- queryClient.invalidateQueries({
529
- queryKey: ["action", "get-document", { id: variables.id }],
530
- });
588
+ queryClient.invalidateQueries(documentQueryFilter(variables.id));
531
589
  queryClient.invalidateQueries({
532
590
  queryKey: ["action", "get-content-database"],
533
591
  });
@@ -606,9 +664,7 @@ export function useMoveDocument() {
606
664
  queryClient.invalidateQueries({
607
665
  queryKey: ["action", "list-documents"],
608
666
  });
609
- queryClient.invalidateQueries({
610
- queryKey: ["action", "get-document", { id: variables.id }],
611
- });
667
+ queryClient.invalidateQueries(documentQueryFilter(variables.id));
612
668
  },
613
669
  },
614
670
  );