@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
@@ -1,10 +1,12 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { assertAccess } from "@agent-native/core/sharing";
3
+ import { eq } from "drizzle-orm";
3
4
  import { z } from "zod";
4
5
 
6
+ import { getDb, schema } from "../server/db/index.js";
5
7
  import type {
6
8
  BuilderCmsModelFieldSummary,
7
- ContentDatabaseResponse,
9
+ ContentDatabaseSourceAttachmentResult,
8
10
  ContentDatabaseSourceFederation,
9
11
  ContentDatabaseSourceType,
10
12
  } from "../shared/api.js";
@@ -41,11 +43,13 @@ import {
41
43
  readLocalTableEntries,
42
44
  resolveReadableLocalTableSource,
43
45
  } from "./_local-table-source.js";
46
+ import { listPropertiesForDatabase } from "./_property-utils.js";
44
47
 
45
48
  const sourceTypeSchema = z
46
49
  .enum(["mock-local", "builder-cms", "local-table", "notion-database"])
47
50
  .default("mock-local");
48
51
  const BUILDER_CMS_ATTACH_INITIAL_PAGES = 1;
52
+ const BUILDER_CMS_ATTACH_METADATA_LIMIT = 10_000;
49
53
 
50
54
  export async function readInitialBuilderCmsAttachEntries(
51
55
  sourceTable: string,
@@ -64,24 +68,43 @@ export async function readInitialBuilderCmsAttachSource(
64
68
  dependencies: {
65
69
  readModelFields?: typeof readBuilderCmsModelFields;
66
70
  readEntries?: typeof readBuilderCmsContentEntries;
71
+ fieldPaths?: readonly string[];
67
72
  } = {},
68
73
  ) {
69
74
  const readModelFields =
70
75
  dependencies.readModelFields ?? readBuilderCmsModelFields;
71
76
  const readEntries = dependencies.readEntries ?? readBuilderCmsContentEntries;
72
- let modelFields: BuilderCmsModelFieldSummary[] = [];
73
- let modelFieldsError: unknown = null;
74
- try {
75
- modelFields = await readModelFields({ model: sourceTable });
76
- } catch (error) {
77
- modelFieldsError = error;
78
- }
79
- const read = await readInitialBuilderCmsAttachEntries(
80
- sourceTable,
81
- readEntries,
82
- modelFields.map((field) => `data.${field.name}`),
83
- );
84
- if (modelFieldsError) throw modelFieldsError;
77
+ const [modelFields, read] = await Promise.all([
78
+ readModelFields({ model: sourceTable }),
79
+ readInitialBuilderCmsAttachEntries(
80
+ sourceTable,
81
+ readEntries,
82
+ dependencies.fieldPaths,
83
+ ),
84
+ ]);
85
+ return { read, modelFields };
86
+ }
87
+
88
+ export async function readCompleteBuilderCmsAttachSource(
89
+ sourceTable: string,
90
+ dependencies: {
91
+ readModelFields?: typeof readBuilderCmsModelFields;
92
+ readEntries?: typeof readBuilderCmsContentEntries;
93
+ fieldPaths?: readonly string[];
94
+ } = {},
95
+ ) {
96
+ const readModelFields =
97
+ dependencies.readModelFields ?? readBuilderCmsModelFields;
98
+ const readEntries = dependencies.readEntries ?? readBuilderCmsContentEntries;
99
+ const [modelFields, read] = await Promise.all([
100
+ readModelFields({ model: sourceTable }),
101
+ readEntries({
102
+ model: sourceTable,
103
+ fieldPaths: dependencies.fieldPaths,
104
+ allowCached: true,
105
+ limit: BUILDER_CMS_ATTACH_METADATA_LIMIT,
106
+ }),
107
+ ]);
85
108
  return { read, modelFields };
86
109
  }
87
110
 
@@ -122,6 +145,32 @@ export function builderCmsAttachReadMetadata(read: BuilderCmsReadResult) {
122
145
  };
123
146
  }
124
147
 
148
+ export function initialBuilderAttachmentSetupOptions(args: {
149
+ builderRead: BuilderCmsReadResult | null;
150
+ importedEntriesByDocumentId: ReadonlyMap<string, BuilderCmsSourceEntry>;
151
+ }) {
152
+ if (
153
+ args.builderRead?.state !== "live" ||
154
+ args.importedEntriesByDocumentId.size !== args.builderRead.entries.length
155
+ ) {
156
+ return undefined;
157
+ }
158
+ const documentIds = [...args.importedEntriesByDocumentId.keys()];
159
+ return {
160
+ documentIds,
161
+ limit: Math.max(1, documentIds.length),
162
+ offset: 0,
163
+ };
164
+ }
165
+
166
+ export function builderAttachDurableItemCount(
167
+ builderEntriesByDocumentId:
168
+ | ReadonlyMap<string, BuilderCmsSourceEntry>
169
+ | undefined,
170
+ ) {
171
+ return builderEntriesByDocumentId?.size ?? 0;
172
+ }
173
+
125
174
  // Per-source key mapping the UI commits after the canonical-key confirm step.
126
175
  const normalizationFormulaSchema = z
127
176
  .string()
@@ -211,6 +260,7 @@ export default defineAction({
211
260
  .string()
212
261
  .optional()
213
262
  .describe("Source table/model name, for example content_items."),
263
+ builderFieldPaths: z.array(z.string().max(500)).max(200).optional(),
214
264
  relationshipMode: z
215
265
  .enum(["items", "details"])
216
266
  .optional()
@@ -231,7 +281,7 @@ export default defineAction({
231
281
  limit: z.coerce.number().int().min(1).max(500).default(100),
232
282
  offset: z.coerce.number().int().min(0).default(0),
233
283
  }),
234
- run: async (args): Promise<ContentDatabaseResponse> => {
284
+ run: async (args): Promise<ContentDatabaseSourceAttachmentResult> => {
235
285
  const database = await resolveDatabaseForSourceMutation(args);
236
286
  if (!database) throw new Error("Database not found.");
237
287
  await assertAccess("document", database.documentId, "editor");
@@ -445,11 +495,14 @@ export default defineAction({
445
495
  sourceTable,
446
496
  now,
447
497
  });
448
- // Snapshot existing items BEFORE importing so we can bind the new source
449
- // to ONLY the rows it imports — never the primary's existing rows.
450
- const beforeSetup = await sourceSetupPayload(database.id);
498
+ // Snapshot membership IDs before importing so the new source binds only
499
+ // its own rows without serializing the existing database.
500
+ const priorItems = await getDb()
501
+ .select({ documentId: schema.contentDatabaseItems.documentId })
502
+ .from(schema.contentDatabaseItems)
503
+ .where(eq(schema.contentDatabaseItems.databaseId, database.id));
451
504
  const priorDocumentIds = new Set(
452
- beforeSetup.response.items.map((item) => item.document.id),
505
+ priorItems.map((item) => item.documentId),
453
506
  );
454
507
  let importedEntriesByDocumentId = new Map<
455
508
  string,
@@ -466,7 +519,12 @@ export default defineAction({
466
519
  });
467
520
  importedEntriesByDocumentId = importResult.importedEntriesByDocumentId;
468
521
  }
469
- const additionalSetup = await sourceSetupPayload(database.id);
522
+ const importedDocumentIds = [...importedEntriesByDocumentId.keys()];
523
+ const additionalSetup = await sourceSetupPayload(database.id, {
524
+ documentIds: importedDocumentIds,
525
+ limit: Math.max(1, importedDocumentIds.length),
526
+ offset: 0,
527
+ });
470
528
  // Only the items this collection just created — exclude the primary's.
471
529
  const importedItems = additionalSetup.response.items.filter(
472
530
  (item) => !priorDocumentIds.has(item.document.id),
@@ -550,7 +608,9 @@ export default defineAction({
550
608
  : [];
551
609
  const builderInitial =
552
610
  sourceType === "builder-cms"
553
- ? await readInitialBuilderCmsAttachSource(sourceTable)
611
+ ? await readCompleteBuilderCmsAttachSource(sourceTable, {
612
+ fieldPaths: args.builderFieldPaths,
613
+ })
554
614
  : null;
555
615
  const sourceId = await replaceSourceMetadata({
556
616
  database,
@@ -562,9 +622,15 @@ export default defineAction({
562
622
  });
563
623
  const builderModelFields = builderInitial?.modelFields ?? [];
564
624
  const builderRead = builderInitial?.read ?? null;
625
+ const builderPropertiesPromise = builderRead
626
+ ? listPropertiesForDatabase(database.id)
627
+ : null;
565
628
  const builderEntries =
566
629
  builderRead?.state === "live" ? builderRead.entries : [];
567
630
  let importedEntriesByDocumentId = new Map<string, BuilderCmsSourceEntry>();
631
+ let importedItems: Awaited<
632
+ ReturnType<typeof importBuilderCmsEntriesAsDatabaseItems>
633
+ >["importedItems"] = [];
568
634
  if (builderRead?.state === "live") {
569
635
  const importResult = await importBuilderCmsEntriesAsDatabaseItems({
570
636
  database,
@@ -574,14 +640,29 @@ export default defineAction({
574
640
  existingSourceRows,
575
641
  });
576
642
  importedEntriesByDocumentId = importResult.importedEntriesByDocumentId;
643
+ importedItems = importResult.importedItems;
577
644
  }
578
-
579
- const refreshedSetup = await sourceSetupPayload(database.id);
645
+ const canUseImportedBuilderItems =
646
+ builderRead?.state === "live" &&
647
+ importedItems.length === builderRead.entries.length &&
648
+ importedItems.length === importedEntriesByDocumentId.size;
649
+ const refreshedSetup = canUseImportedBuilderItems
650
+ ? null
651
+ : await sourceSetupPayload(
652
+ database.id,
653
+ initialBuilderAttachmentSetupOptions({
654
+ builderRead,
655
+ importedEntriesByDocumentId,
656
+ }),
657
+ );
658
+ const refreshedProperties =
659
+ refreshedSetup?.properties ?? (await builderPropertiesPromise) ?? [];
660
+ const refreshedItems = refreshedSetup?.response.items ?? importedItems;
580
661
  const builderEntriesByDocumentId =
581
662
  builderRead?.state === "live"
582
663
  ? mapBuilderCmsEntriesToLocalItems({
583
664
  entries: builderEntries,
584
- items: refreshedSetup.response.items,
665
+ items: refreshedItems,
585
666
  sourceTable,
586
667
  now,
587
668
  existingRows: existingSourceRows,
@@ -591,48 +672,65 @@ export default defineAction({
591
672
  builderEntriesByDocumentId?.set(documentId, entry);
592
673
  }
593
674
 
594
- await seedMockSourceFields({
595
- sourceId,
596
- ownerEmail: database.ownerEmail,
597
- sourceType,
598
- properties: refreshedSetup.properties,
599
- builderModelFields,
600
- builderSampleEntries: builderEntries,
601
- now,
602
- });
603
- await seedMockSourceRows({
604
- sourceId,
605
- ownerEmail: database.ownerEmail,
606
- sourceType,
607
- sourceTable,
608
- items: refreshedSetup.response.items,
609
- now,
610
- builderEntriesByDocumentId,
611
- });
612
- if (sourceType === "builder-cms" && builderRead?.state === "live") {
613
- await enqueueBuilderBodyHydrationForItems({
675
+ await (async () => {
676
+ await seedMockSourceFields({
614
677
  sourceId,
615
678
  ownerEmail: database.ownerEmail,
616
- orgId: database.orgId,
617
- sourceTable,
618
- items: refreshedSetup.response.items,
619
- builderEntriesByDocumentId,
679
+ sourceType,
680
+ properties: refreshedProperties,
681
+ builderModelFields,
682
+ builderSampleEntries: builderEntries,
620
683
  now,
621
684
  });
622
- }
623
- if (sourceType === "builder-cms" && builderRead) {
624
- await updateBuilderCmsSourceReadMetadata({
685
+ await seedMockSourceRows({
625
686
  sourceId,
687
+ ownerEmail: database.ownerEmail,
688
+ sourceType,
626
689
  sourceTable,
627
- readState: builderRead.state,
628
- entryCount: builderRead.entries.length,
629
- matchedRowCount: builderEntriesByDocumentId?.size ?? 0,
630
- fetchedAt: builderRead.fetchedAt,
690
+ items: refreshedItems,
631
691
  now,
632
- message: builderRead.message,
633
- builderModelFields,
634
- ...builderCmsAttachReadMetadata(builderRead),
692
+ builderEntriesByDocumentId,
635
693
  });
694
+ if (sourceType === "builder-cms" && builderRead?.state === "live") {
695
+ await enqueueBuilderBodyHydrationForItems({
696
+ sourceId,
697
+ ownerEmail: database.ownerEmail,
698
+ orgId: database.orgId,
699
+ sourceTable,
700
+ items: refreshedItems,
701
+ builderEntriesByDocumentId,
702
+ now,
703
+ processInBackground: false,
704
+ });
705
+ }
706
+ if (sourceType === "builder-cms" && builderRead) {
707
+ await updateBuilderCmsSourceReadMetadata({
708
+ sourceId,
709
+ sourceTable,
710
+ readState: builderRead.state,
711
+ entryCount: builderRead.entries.length,
712
+ matchedRowCount: builderEntriesByDocumentId?.size ?? 0,
713
+ fetchedAt: builderRead.fetchedAt,
714
+ now,
715
+ message: builderRead.message,
716
+ builderModelFields,
717
+ ...builderCmsAttachReadMetadata(builderRead),
718
+ });
719
+ }
720
+ })();
721
+ if (sourceType === "builder-cms") {
722
+ return {
723
+ responseProjection: "ack",
724
+ databaseId: database.id,
725
+ documentId: database.documentId,
726
+ sourceId,
727
+ sourceType,
728
+ sourceTable,
729
+ importedItemCount: builderAttachDurableItemCount(
730
+ builderEntriesByDocumentId,
731
+ ),
732
+ fetchedAt: builderRead?.fetchedAt ?? now,
733
+ };
636
734
  }
637
735
 
638
736
  return getContentDatabaseResponse(database.id, {
@@ -105,7 +105,7 @@ export default defineAction({
105
105
  .update(schema.contentDatabaseSources)
106
106
  .set({ updatedAt: now })
107
107
  .where(eq(schema.contentDatabaseSources.id, source.id));
108
- return getContentDatabaseResponse(database.id);
108
+ return getContentDatabaseResponse(database.id, { limit: 100, offset: 0 });
109
109
  }
110
110
 
111
111
  // ── Bind to an existing column ─────────────────────────────────────────
@@ -261,6 +261,6 @@ export default defineAction({
261
261
  }
262
262
  }
263
263
 
264
- return getContentDatabaseResponse(database.id);
264
+ return getContentDatabaseResponse(database.id, { limit: 100, offset: 0 });
265
265
  },
266
266
  });
@@ -295,7 +295,10 @@ export default defineAction({
295
295
  });
296
296
 
297
297
  return {
298
- ...(await getContentDatabaseResponse(database.id)),
298
+ ...(await getContentDatabaseResponse(database.id, {
299
+ limit: 100,
300
+ offset: 0,
301
+ })),
299
302
  cancellation: {
300
303
  sourceId: args.sourceId,
301
304
  changeSetId: args.changeSetId,
@@ -38,7 +38,10 @@ export default defineAction({
38
38
  });
39
39
  await writeAppState("refresh-signal", { ts: Date.now() });
40
40
  return {
41
- ...(await getContentDatabaseResponse(database.id)),
41
+ ...(await getContentDatabaseResponse(database.id, {
42
+ limit: 100,
43
+ offset: 0,
44
+ })),
42
45
  deletedItemIds: removedItemIds,
43
46
  deletedDocumentIds: [],
44
47
  deletedCount: 0,
@@ -74,7 +77,10 @@ export default defineAction({
74
77
  await writeAppState("refresh-signal", { ts: Date.now() });
75
78
 
76
79
  return {
77
- ...(await getContentDatabaseResponse(database.id)),
80
+ ...(await getContentDatabaseResponse(database.id, {
81
+ limit: 100,
82
+ offset: 0,
83
+ })),
78
84
  deletedItemIds,
79
85
  deletedDocumentIds,
80
86
  deletedCount: deletedItemIds.length,
@@ -71,7 +71,7 @@ export default defineAction({
71
71
  ),
72
72
  );
73
73
  if (target) await deleteSourceRecords(target.id);
74
- return getContentDatabaseResponse(database.id);
74
+ return getContentDatabaseResponse(database.id, { limit: 100, offset: 0 });
75
75
  }
76
76
 
77
77
  const source = await getExistingSource(database.id);
@@ -79,6 +79,6 @@ export default defineAction({
79
79
  await deleteSourceRecords(source.id);
80
80
  }
81
81
 
82
- return getContentDatabaseResponse(database.id);
82
+ return getContentDatabaseResponse(database.id, { limit: 100, offset: 0 });
83
83
  },
84
84
  });
@@ -176,8 +176,22 @@ export default defineAction({
176
176
 
177
177
  await writeAppState("refresh-signal", { ts: Date.now() });
178
178
 
179
+ const response = await getContentDatabaseResponse(row.item.databaseId, {
180
+ limit: 100,
181
+ offset: 0,
182
+ });
183
+ const duplicatedItem =
184
+ response.items.find((item) => item.id === nextItemId) ??
185
+ (
186
+ await getContentDatabaseResponse(row.item.databaseId, {
187
+ limit: 1,
188
+ offset: 0,
189
+ documentIds: [nextDocumentId],
190
+ })
191
+ ).items.find((item) => item.id === nextItemId);
179
192
  return {
180
- ...(await getContentDatabaseResponse(row.item.databaseId)),
193
+ ...response,
194
+ duplicatedItems: duplicatedItem ? [duplicatedItem] : [],
181
195
  duplicatedItemId: nextItemId,
182
196
  duplicatedDocumentId: nextDocumentId,
183
197
  };
@@ -186,8 +186,26 @@ export default defineAction({
186
186
 
187
187
  await writeAppState("refresh-signal", { ts: Date.now() });
188
188
 
189
+ const response = await getContentDatabaseResponse(database.id, {
190
+ limit: 100,
191
+ offset: 0,
192
+ });
193
+ const duplicatedPage = await getContentDatabaseResponse(database.id, {
194
+ limit: duplicates.length,
195
+ offset: 0,
196
+ documentIds: duplicates.map(
197
+ (duplicate) => duplicate.duplicatedDocumentId,
198
+ ),
199
+ });
200
+ const duplicatedItemIds = new Set(
201
+ duplicates.map((duplicate) => duplicate.duplicatedItemId),
202
+ );
203
+ const duplicatedItems = duplicatedPage.items.filter((item) =>
204
+ duplicatedItemIds.has(item.id),
205
+ );
189
206
  return {
190
- ...(await getContentDatabaseResponse(database.id)),
207
+ ...response,
208
+ duplicatedItems,
191
209
  duplicatedItemId: duplicates[0]?.duplicatedItemId,
192
210
  duplicatedDocumentId: duplicates[0]?.duplicatedDocumentId,
193
211
  duplicatedItemIds: duplicates.map(
@@ -854,7 +854,8 @@ export function realExecutionDeps(
854
854
  executeWrite: (args) => executeBuilderCmsWrite(args),
855
855
  readLiveEntry: (args) => readBuilderCmsEntryLiveState(args),
856
856
  reconcileWrite: reconcileBuilderCmsWrite,
857
- getResponse: (databaseId) => getContentDatabaseResponse(databaseId),
857
+ getResponse: (databaseId) =>
858
+ getContentDatabaseResponse(databaseId, { limit: 100, offset: 0 }),
858
859
  lookupSafeModelIntent: lookupBuilderCmsSafeModelIntent,
859
860
  };
860
861
  }
@@ -1,18 +1,15 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { resolveAccess } from "@agent-native/core/sharing";
3
- import { eq } from "drizzle-orm";
4
2
  import { z } from "zod";
5
3
 
6
- import { getDb, schema } from "../server/db/index.js";
7
4
  import type {
8
5
  ContentDatabaseResponse,
9
6
  ContentDatabaseUnavailableResponse,
10
7
  } from "../shared/api.js";
11
- import { resolveContentSpaceAccess } from "./_content-space-access.js";
12
8
  import {
13
9
  CONTENT_DATABASE_MAX_READ_LIMIT,
10
+ contentDatabaseTableQuerySchema,
14
11
  getContentDatabaseResponse,
15
- getDocumentContextPath,
12
+ resolveContentDatabaseRead,
16
13
  } from "./_database-utils.js";
17
14
 
18
15
  export default defineAction({
@@ -28,6 +25,7 @@ export default defineAction({
28
25
  .max(CONTENT_DATABASE_MAX_READ_LIMIT)
29
26
  .optional(),
30
27
  offset: z.coerce.number().int().min(0).optional(),
28
+ tableQuery: contentDatabaseTableQuerySchema,
31
29
  }),
32
30
  http: { method: "GET" },
33
31
  readOnly: true,
@@ -36,69 +34,19 @@ export default defineAction({
36
34
  documentId,
37
35
  limit,
38
36
  offset,
37
+ tableQuery,
39
38
  }): Promise<ContentDatabaseResponse | ContentDatabaseUnavailableResponse> => {
40
- const db = getDb();
41
- let resolvedDatabaseId = databaseId;
42
-
43
- if (!resolvedDatabaseId && documentId) {
44
- const [database] = await db
45
- .select()
46
- .from(schema.contentDatabases)
47
- .where(eq(schema.contentDatabases.documentId, documentId));
48
- resolvedDatabaseId = database?.id;
49
- }
50
-
51
- if (!resolvedDatabaseId) {
52
- throw new Error("Either databaseId or documentId is required.");
53
- }
54
-
55
- const [database] = await db
56
- .select()
57
- .from(schema.contentDatabases)
58
- .where(eq(schema.contentDatabases.id, resolvedDatabaseId));
59
- if (!database) {
60
- return {
61
- available: false,
62
- reason: "not_found",
63
- databaseId: resolvedDatabaseId,
64
- documentId: documentId ?? null,
65
- message: `Database "${resolvedDatabaseId}" not found`,
66
- };
67
- }
68
-
69
- let canRead = Boolean(await resolveAccess("document", database.documentId));
70
- if (!canRead && database.systemRole === "files" && database.spaceId) {
71
- try {
72
- await resolveContentSpaceAccess(database.spaceId);
73
- canRead = true;
74
- } catch {
75
- canRead = false;
76
- }
77
- }
78
- if (!canRead) throw new Error(`Database "${resolvedDatabaseId}" not found`);
79
-
80
- if (database.deletedAt) {
81
- return {
82
- available: false,
83
- reason: "deleted",
84
- databaseId: database.id,
85
- documentId: database.documentId,
86
- deletedAt: database.deletedAt,
87
- message: `Database "${database.id}" has been deleted`,
88
- };
89
- }
39
+ const resolved = await resolveContentDatabaseRead({
40
+ databaseId,
41
+ documentId,
42
+ });
43
+ if (!resolved.available) return resolved;
90
44
 
91
- const response = await getContentDatabaseResponse(resolvedDatabaseId, {
45
+ return getContentDatabaseResponse(resolved.database.id, {
92
46
  limit,
93
47
  offset,
48
+ tableQuery,
49
+ database: resolved.database,
94
50
  });
95
- const [document] = await db
96
- .select()
97
- .from(schema.documents)
98
- .where(eq(schema.documents.id, database.documentId));
99
- return {
100
- ...response,
101
- contextPath: document ? await getDocumentContextPath(document) : [],
102
- };
103
51
  },
104
52
  });
@@ -135,6 +135,9 @@ export default defineAction({
135
135
 
136
136
  await writeAppState("refresh-signal", { ts: Date.now() });
137
137
 
138
- return getContentDatabaseResponse(row.item.databaseId);
138
+ return getContentDatabaseResponse(row.item.databaseId, {
139
+ limit: 100,
140
+ offset: 0,
141
+ });
139
142
  },
140
143
  });
@@ -155,7 +155,7 @@ export default defineAction({
155
155
  timing.record("gate_preparation_and_dry_run_validation", gateStartedAt);
156
156
 
157
157
  const response = await timing.measure("response_load", () =>
158
- getContentDatabaseResponse(database.id),
158
+ getContentDatabaseResponse(database.id, { limit: 100, offset: 0 }),
159
159
  );
160
160
  const result = { ...response, timings: timing.finish() };
161
161
  timing.log("succeeded");
@@ -34,7 +34,6 @@ import {
34
34
  serializeSourceRowRecord,
35
35
  sourceChangeSetKey,
36
36
  } from "./_database-source-utils.js";
37
- import { getContentDatabaseResponse } from "./_database-utils.js";
38
37
 
39
38
  export const BUILDER_SOURCE_REVIEW_PREPARE_LIMIT = 100;
40
39
 
@@ -763,18 +762,16 @@ export default defineAction({
763
762
  approvalStartedAt,
764
763
  );
765
764
 
766
- const { reviewedSnapshot, response } = await timing.measure(
767
- "reconciliation_and_response_load",
768
- async () => ({
769
- reviewedSnapshot: await getContentDatabaseSourceSnapshotForWrite(
765
+ const reviewedSnapshot = await timing.measure(
766
+ "reconciliation_snapshot_load",
767
+ () =>
768
+ getContentDatabaseSourceSnapshotForWrite(
770
769
  database,
771
770
  args.sourceId,
772
771
  reviewableChanges.every((changeSet) => changeSet.documentId)
773
772
  ? reviewableChanges.map((changeSet) => changeSet.documentId!)
774
773
  : args.documentIds,
775
774
  ),
776
- response: await getContentDatabaseResponse(database.id),
777
- }),
778
775
  );
779
776
  if (!reviewedSnapshot) throw new Error("Builder source disappeared.");
780
777
  // Build the review payload from the TARGET source snapshot, not
@@ -797,7 +794,6 @@ export default defineAction({
797
794
  review.preparedRowLimit = reviewableChanges.length;
798
795
 
799
796
  const result = {
800
- ...response,
801
797
  review,
802
798
  preparedChangeSetMappings,
803
799
  timings: timing.finish(),