@agent-native/core 0.102.2 → 0.103.1
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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/local-file-mode.mdx +99 -489
- package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/corpus/core/docs/content/template-content.mdx +26 -42
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/content-local.ts +86 -18
- package/corpus/core/src/cli/index.ts +2 -2
- package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
- package/corpus/core/src/cli/skills.ts +47 -13
- package/corpus/core/src/local-artifacts/index.ts +65 -0
- package/corpus/core/src/vite/client.ts +2 -0
- package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/assets/app/i18n-data.ts +215 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
- package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
- package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
- package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
- package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
- package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
- package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
- package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
- package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
- package/corpus/templates/clips/server/lib/share-password.ts +6 -3
- package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
- package/corpus/templates/content/AGENTS.md +25 -30
- package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
- package/corpus/templates/content/actions/_content-files.ts +338 -0
- package/corpus/templates/content/actions/_content-space-access.ts +135 -0
- package/corpus/templates/content/actions/_content-spaces.ts +640 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
- package/corpus/templates/content/actions/_database-utils.ts +7 -1
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
- package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
- package/corpus/templates/content/actions/_property-utils.ts +9 -4
- package/corpus/templates/content/actions/add-database-item.ts +108 -65
- package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
- package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
- package/corpus/templates/content/actions/create-content-database.ts +129 -3
- package/corpus/templates/content/actions/create-document.ts +65 -55
- package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
- package/corpus/templates/content/actions/delete-content-database.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +7 -11
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
- package/corpus/templates/content/actions/edit-document.ts +2 -28
- package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
- package/corpus/templates/content/actions/export-content-source.ts +47 -9
- package/corpus/templates/content/actions/get-document.ts +0 -14
- package/corpus/templates/content/actions/import-content-source.ts +43 -3
- package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
- package/corpus/templates/content/actions/list-documents.ts +12 -9
- package/corpus/templates/content/actions/move-document.ts +3 -14
- package/corpus/templates/content/actions/pull-document.ts +0 -23
- package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
- package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
- package/corpus/templates/content/actions/search-documents.ts +0 -34
- package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
- package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
- package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
- package/corpus/templates/content/actions/update-document.ts +0 -15
- package/corpus/templates/content/actions/view-screen.ts +2 -50
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
- package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
- package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
- package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
- package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
- package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
- package/corpus/templates/content/app/i18n-data.ts +75 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
- package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
- package/corpus/templates/content/parity/matrix.md +2 -1
- package/corpus/templates/content/parity/matrix.ts +37 -1
- package/corpus/templates/content/scripts/dev-database.mjs +7 -5
- package/corpus/templates/content/server/db/schema.ts +113 -40
- package/corpus/templates/content/server/lib/document-context.ts +15 -1
- package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
- package/corpus/templates/content/server/plugins/db.ts +153 -0
- package/corpus/templates/content/shared/api.ts +17 -3
- package/corpus/templates/content/shared/builder-mdx.ts +163 -16
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
- package/corpus/templates/design/AGENTS.md +6 -1
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
- package/corpus/templates/design/actions/list-design-templates.ts +69 -21
- package/corpus/templates/design/actions/navigate.ts +2 -2
- package/corpus/templates/design/actions/view-screen.ts +15 -2
- package/corpus/templates/design/agent-native.app-skill.json +5 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/design/app/i18n-data.ts +21 -2
- package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
- package/corpus/templates/design/app/pages/Index.tsx +195 -24
- package/corpus/templates/design/app/pages/Templates.tsx +144 -55
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
- package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/mail/AGENTS.md +23 -18
- package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
- package/corpus/templates/mail/actions/mark-read.ts +131 -20
- package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
- package/corpus/templates/mail/actions/move-email.ts +30 -24
- package/corpus/templates/mail/actions/send-email.ts +73 -68
- package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +218 -209
- package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
- package/corpus/templates/mail/server/lib/email-state.ts +198 -113
- package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
- package/corpus/templates/mail/server/lib/jobs.ts +69 -74
- package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/dist/cli/content-local.d.ts +1 -0
- package/dist/cli/content-local.d.ts.map +1 -1
- package/dist/cli/content-local.js +75 -17
- package/dist/cli/content-local.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/content-skill.d.ts +1 -1
- package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/content-skill.js +36 -25
- package/dist/cli/skills-content/content-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +47 -14
- package/dist/cli/skills.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/local-artifacts/index.d.ts +10 -0
- package/dist/local-artifacts/index.d.ts.map +1 -1
- package/dist/local-artifacts/index.js +39 -0
- package/dist/local-artifacts/index.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/local-file-mode.mdx +99 -489
- package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/docs/content/template-content.mdx +26 -42
- package/package.json +1 -1
- package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
|
+
import { and, eq, inArray, ne } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
8
|
+
import { LOCAL_FOLDER_SOURCE_TYPE } from "./_local-folder-source.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description:
|
|
12
|
+
"Disconnect a local-folder source without deleting its local files or the SQL-backed Content pages it materialized.",
|
|
13
|
+
schema: z.object({ sourceId: z.string().min(1) }),
|
|
14
|
+
run: async ({ sourceId }) => {
|
|
15
|
+
const db = getDb();
|
|
16
|
+
const [target] = await db
|
|
17
|
+
.select({
|
|
18
|
+
source: schema.contentDatabaseSources,
|
|
19
|
+
database: schema.contentDatabases,
|
|
20
|
+
})
|
|
21
|
+
.from(schema.contentDatabaseSources)
|
|
22
|
+
.innerJoin(
|
|
23
|
+
schema.contentDatabases,
|
|
24
|
+
eq(
|
|
25
|
+
schema.contentDatabases.id,
|
|
26
|
+
schema.contentDatabaseSources.databaseId,
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
.where(eq(schema.contentDatabaseSources.id, sourceId));
|
|
30
|
+
if (
|
|
31
|
+
!target ||
|
|
32
|
+
target.source.sourceType !== LOCAL_FOLDER_SOURCE_TYPE ||
|
|
33
|
+
!target.database.spaceId
|
|
34
|
+
) {
|
|
35
|
+
throw new Error(`Local folder source "${sourceId}" not found`);
|
|
36
|
+
}
|
|
37
|
+
await resolveContentSpaceAccess(target.database.spaceId, "editor");
|
|
38
|
+
const spaceId = target.database.spaceId;
|
|
39
|
+
let disconnectedDocuments = 0;
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
|
|
42
|
+
await db.transaction(async (tx: any) => {
|
|
43
|
+
const rows = await tx
|
|
44
|
+
.select({ documentId: schema.contentDatabaseSourceRows.documentId })
|
|
45
|
+
.from(schema.contentDatabaseSourceRows)
|
|
46
|
+
.where(eq(schema.contentDatabaseSourceRows.sourceId, sourceId));
|
|
47
|
+
const documentIds: string[] = [
|
|
48
|
+
...new Set<string>(rows.map((row: any) => String(row.documentId))),
|
|
49
|
+
];
|
|
50
|
+
disconnectedDocuments = documentIds.length;
|
|
51
|
+
await tx
|
|
52
|
+
.delete(schema.contentDatabaseSourceExecutions)
|
|
53
|
+
.where(eq(schema.contentDatabaseSourceExecutions.sourceId, sourceId));
|
|
54
|
+
await tx
|
|
55
|
+
.delete(schema.contentDatabaseSourceExecutionClaims)
|
|
56
|
+
.where(
|
|
57
|
+
eq(schema.contentDatabaseSourceExecutionClaims.sourceId, sourceId),
|
|
58
|
+
);
|
|
59
|
+
await tx
|
|
60
|
+
.delete(schema.contentDatabaseSourceChangeReviews)
|
|
61
|
+
.where(
|
|
62
|
+
eq(schema.contentDatabaseSourceChangeReviews.sourceId, sourceId),
|
|
63
|
+
);
|
|
64
|
+
await tx
|
|
65
|
+
.delete(schema.contentDatabaseSourceChangeSets)
|
|
66
|
+
.where(eq(schema.contentDatabaseSourceChangeSets.sourceId, sourceId));
|
|
67
|
+
await tx
|
|
68
|
+
.delete(schema.contentDatabaseBodyHydrationQueue)
|
|
69
|
+
.where(eq(schema.contentDatabaseBodyHydrationQueue.sourceId, sourceId));
|
|
70
|
+
await tx
|
|
71
|
+
.delete(schema.contentDatabaseSourceFields)
|
|
72
|
+
.where(eq(schema.contentDatabaseSourceFields.sourceId, sourceId));
|
|
73
|
+
await tx
|
|
74
|
+
.delete(schema.contentDatabaseSourceRows)
|
|
75
|
+
.where(eq(schema.contentDatabaseSourceRows.sourceId, sourceId));
|
|
76
|
+
await tx
|
|
77
|
+
.delete(schema.contentDatabaseSources)
|
|
78
|
+
.where(eq(schema.contentDatabaseSources.id, sourceId));
|
|
79
|
+
type RemainingLocalRow = {
|
|
80
|
+
documentId: string;
|
|
81
|
+
sourceDisplayKey: string | null;
|
|
82
|
+
sourceValuesJson: string | null;
|
|
83
|
+
sourceName: string;
|
|
84
|
+
};
|
|
85
|
+
const remainingLocalRows: RemainingLocalRow[] = documentIds.length
|
|
86
|
+
? await tx
|
|
87
|
+
.select({
|
|
88
|
+
documentId: schema.contentDatabaseSourceRows.documentId,
|
|
89
|
+
sourceDisplayKey:
|
|
90
|
+
schema.contentDatabaseSourceRows.sourceDisplayKey,
|
|
91
|
+
sourceValuesJson:
|
|
92
|
+
schema.contentDatabaseSourceRows.sourceValuesJson,
|
|
93
|
+
sourceName: schema.contentDatabaseSources.sourceName,
|
|
94
|
+
})
|
|
95
|
+
.from(schema.contentDatabaseSourceRows)
|
|
96
|
+
.innerJoin(
|
|
97
|
+
schema.contentDatabaseSources,
|
|
98
|
+
eq(
|
|
99
|
+
schema.contentDatabaseSources.id,
|
|
100
|
+
schema.contentDatabaseSourceRows.sourceId,
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
.where(
|
|
104
|
+
and(
|
|
105
|
+
inArray(
|
|
106
|
+
schema.contentDatabaseSourceRows.documentId,
|
|
107
|
+
documentIds,
|
|
108
|
+
),
|
|
109
|
+
ne(schema.contentDatabaseSourceRows.sourceId, sourceId),
|
|
110
|
+
eq(
|
|
111
|
+
schema.contentDatabaseSources.sourceType,
|
|
112
|
+
LOCAL_FOLDER_SOURCE_TYPE,
|
|
113
|
+
),
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
: [];
|
|
117
|
+
const remainingLocalRowByDocument = new Map<string, RemainingLocalRow>(
|
|
118
|
+
remainingLocalRows.map((row) => [row.documentId, row]),
|
|
119
|
+
);
|
|
120
|
+
const documentsToClear = documentIds.filter(
|
|
121
|
+
(documentId) => !remainingLocalRowByDocument.has(documentId),
|
|
122
|
+
);
|
|
123
|
+
if (documentsToClear.length) {
|
|
124
|
+
await tx
|
|
125
|
+
.update(schema.documents)
|
|
126
|
+
.set({
|
|
127
|
+
sourceMode: "database",
|
|
128
|
+
sourceKind: null,
|
|
129
|
+
sourcePath: null,
|
|
130
|
+
sourceRootPath: null,
|
|
131
|
+
sourceUpdatedAt: now,
|
|
132
|
+
updatedAt: now,
|
|
133
|
+
})
|
|
134
|
+
.where(
|
|
135
|
+
and(
|
|
136
|
+
inArray(schema.documents.id, documentsToClear),
|
|
137
|
+
eq(schema.documents.spaceId, spaceId),
|
|
138
|
+
),
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
for (const [documentId, remaining] of remainingLocalRowByDocument) {
|
|
142
|
+
const sourceValues = JSON.parse(remaining.sourceValuesJson || "{}") as {
|
|
143
|
+
relativePath?: unknown;
|
|
144
|
+
};
|
|
145
|
+
const relativePath =
|
|
146
|
+
typeof sourceValues.relativePath === "string"
|
|
147
|
+
? sourceValues.relativePath
|
|
148
|
+
: remaining.sourceDisplayKey;
|
|
149
|
+
await tx
|
|
150
|
+
.update(schema.documents)
|
|
151
|
+
.set({
|
|
152
|
+
sourceMode: "local-files",
|
|
153
|
+
sourceKind: "file",
|
|
154
|
+
sourcePath: relativePath,
|
|
155
|
+
sourceRootPath: remaining.sourceName,
|
|
156
|
+
sourceUpdatedAt: now,
|
|
157
|
+
updatedAt: now,
|
|
158
|
+
})
|
|
159
|
+
.where(
|
|
160
|
+
and(
|
|
161
|
+
eq(schema.documents.id, documentId),
|
|
162
|
+
eq(schema.documents.spaceId, spaceId),
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
sourceId,
|
|
171
|
+
disconnectedDocuments,
|
|
172
|
+
localFilesDeleted: 0,
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
});
|
|
@@ -6,6 +6,7 @@ import { and, eq, gte, isNull, sql } from "drizzle-orm";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
|
|
8
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
|
+
import { ensureDocumentFilesMembership } from "./_content-files.js";
|
|
9
10
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
10
11
|
import { nanoid } from "./_property-utils.js";
|
|
11
12
|
|
|
@@ -48,6 +49,12 @@ export default defineAction({
|
|
|
48
49
|
);
|
|
49
50
|
|
|
50
51
|
if (!row) throw new Error("Database row not found.");
|
|
52
|
+
if (!row.database.spaceId) {
|
|
53
|
+
throw new Error("Database does not belong to a Content space.");
|
|
54
|
+
}
|
|
55
|
+
if (row.document.spaceId !== row.database.spaceId) {
|
|
56
|
+
throw new Error("Cannot duplicate a database row across Content spaces.");
|
|
57
|
+
}
|
|
51
58
|
|
|
52
59
|
await assertAccess("document", row.database.documentId, "editor");
|
|
53
60
|
await assertAccess("document", row.document.id, "viewer");
|
|
@@ -103,6 +110,7 @@ export default defineAction({
|
|
|
103
110
|
|
|
104
111
|
await tx.insert(schema.documents).values({
|
|
105
112
|
id: nextDocumentId,
|
|
113
|
+
spaceId: row.database.spaceId,
|
|
106
114
|
ownerEmail: row.document.ownerEmail,
|
|
107
115
|
orgId: row.document.orgId,
|
|
108
116
|
parentId: row.database.documentId,
|
|
@@ -155,6 +163,8 @@ export default defineAction({
|
|
|
155
163
|
})),
|
|
156
164
|
);
|
|
157
165
|
}
|
|
166
|
+
|
|
167
|
+
await ensureDocumentFilesMembership(tx, nextDocumentId, now);
|
|
158
168
|
});
|
|
159
169
|
|
|
160
170
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
@@ -5,6 +5,7 @@ import { assertAccess } from "@agent-native/core/sharing";
|
|
|
5
5
|
import { and, eq, gte, inArray, sql } from "drizzle-orm";
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
8
|
+
import { ensureDocumentsFilesMembership } from "./_content-files.js";
|
|
8
9
|
import {
|
|
9
10
|
databaseRowBatchSchema,
|
|
10
11
|
resolveDatabaseRowsForBatch,
|
|
@@ -19,6 +20,12 @@ export default defineAction({
|
|
|
19
20
|
run: async (args) => {
|
|
20
21
|
const db = getDb();
|
|
21
22
|
const { database, rows } = await resolveDatabaseRowsForBatch(args);
|
|
23
|
+
if (!database.spaceId) {
|
|
24
|
+
throw new Error("Database does not belong to a Content space.");
|
|
25
|
+
}
|
|
26
|
+
if (rows.some((row) => row.document.spaceId !== database.spaceId)) {
|
|
27
|
+
throw new Error("Cannot duplicate database rows across Content spaces.");
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
await assertAccess("document", database.documentId, "editor");
|
|
24
31
|
for (const row of rows) {
|
|
@@ -103,6 +110,7 @@ export default defineAction({
|
|
|
103
110
|
await tx.insert(schema.documents).values(
|
|
104
111
|
duplicates.map((duplicate) => ({
|
|
105
112
|
id: duplicate.duplicatedDocumentId,
|
|
113
|
+
spaceId: database.spaceId,
|
|
106
114
|
ownerEmail: duplicate.row.document.ownerEmail,
|
|
107
115
|
orgId: duplicate.row.document.orgId,
|
|
108
116
|
parentId: database.documentId,
|
|
@@ -163,6 +171,11 @@ export default defineAction({
|
|
|
163
171
|
),
|
|
164
172
|
);
|
|
165
173
|
}
|
|
174
|
+
await ensureDocumentsFilesMembership(
|
|
175
|
+
tx,
|
|
176
|
+
duplicates.map((duplicate) => duplicate.duplicatedDocumentId),
|
|
177
|
+
now,
|
|
178
|
+
);
|
|
166
179
|
});
|
|
167
180
|
|
|
168
181
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
@@ -10,11 +10,6 @@ import { eq } from "drizzle-orm";
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
12
|
import { getDb, schema } from "../server/db/index.js";
|
|
13
|
-
import {
|
|
14
|
-
getLocalFileDocument,
|
|
15
|
-
isContentLocalFileMode,
|
|
16
|
-
updateLocalFileDocument,
|
|
17
|
-
} from "./_local-file-documents.js";
|
|
18
13
|
|
|
19
14
|
interface TextEdit {
|
|
20
15
|
find: string;
|
|
@@ -72,19 +67,8 @@ export default defineAction({
|
|
|
72
67
|
if (edit.replace === undefined) edit.replace = "";
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
const
|
|
76
|
-
const existing =
|
|
77
|
-
if (localFileMode) {
|
|
78
|
-
const doc = await getLocalFileDocument(id);
|
|
79
|
-
if (doc.source?.kind === "folder") {
|
|
80
|
-
throw new Error("Folders cannot be edited directly");
|
|
81
|
-
}
|
|
82
|
-
return doc;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const access = await assertAccess("document", id, "editor");
|
|
86
|
-
return access.resource;
|
|
87
|
-
})();
|
|
70
|
+
const access = await assertAccess("document", id, "editor");
|
|
71
|
+
const existing = access.resource;
|
|
88
72
|
|
|
89
73
|
// ─── Apply edits to the document markdown ───────────────────────────────
|
|
90
74
|
//
|
|
@@ -128,16 +112,6 @@ export default defineAction({
|
|
|
128
112
|
return { applied: 0, total: edits.length, results };
|
|
129
113
|
}
|
|
130
114
|
|
|
131
|
-
if (localFileMode) {
|
|
132
|
-
await updateLocalFileDocument(id, { content });
|
|
133
|
-
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
134
|
-
return {
|
|
135
|
-
applied: changeCount,
|
|
136
|
-
total: edits.length,
|
|
137
|
-
results,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
115
|
// Persist. The fresh updatedAt is the signal the open editor uses to tell an
|
|
142
116
|
// intentional external edit apart from a stale autosave echo.
|
|
143
117
|
const db = getDb();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { getDb } from "../server/db/index.js";
|
|
7
|
+
import { reconcileContentFilesMemberships } from "./_content-files.js";
|
|
8
|
+
import { provisionContentSpaces } from "./_content-spaces.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description:
|
|
12
|
+
"Provision and reconcile the signed-in user's personal and organization Content spaces.",
|
|
13
|
+
schema: z.object({}),
|
|
14
|
+
run: async () => {
|
|
15
|
+
const userEmail = getRequestUserEmail();
|
|
16
|
+
if (!userEmail) throw new Error("no authenticated user");
|
|
17
|
+
const db = getDb();
|
|
18
|
+
const result = await provisionContentSpaces(db, userEmail);
|
|
19
|
+
const reconciliation = await reconcileContentFilesMemberships(
|
|
20
|
+
db,
|
|
21
|
+
userEmail,
|
|
22
|
+
);
|
|
23
|
+
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
24
|
+
return { ...result, reconciliation };
|
|
25
|
+
},
|
|
26
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { accessFilter } from "@agent-native/core/sharing";
|
|
3
|
-
import { asc } from "drizzle-orm";
|
|
3
|
+
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -10,13 +10,21 @@ import {
|
|
|
10
10
|
} from "../server/lib/documents.js";
|
|
11
11
|
import {
|
|
12
12
|
buildContentSourceBundle,
|
|
13
|
+
serializeContentSourceDocument,
|
|
13
14
|
type ContentSourceDocument,
|
|
14
15
|
} from "../shared/content-source.js";
|
|
15
16
|
|
|
16
17
|
export default defineAction({
|
|
17
18
|
description:
|
|
18
19
|
"Export editable Content documents as source-control friendly Markdown/MDX files with frontmatter.",
|
|
19
|
-
schema: z.object({
|
|
20
|
+
schema: z.object({
|
|
21
|
+
sourceId: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe(
|
|
25
|
+
"Connected local-folder source to export using its relative paths",
|
|
26
|
+
),
|
|
27
|
+
}),
|
|
20
28
|
http: { method: "GET" },
|
|
21
29
|
readOnly: true,
|
|
22
30
|
publicAgent: {
|
|
@@ -27,11 +35,28 @@ export default defineAction({
|
|
|
27
35
|
description:
|
|
28
36
|
"Export Content documents as a local-file source bundle for MDX workflows.",
|
|
29
37
|
},
|
|
30
|
-
run: async () => {
|
|
31
|
-
const
|
|
38
|
+
run: async ({ sourceId }) => {
|
|
39
|
+
const db = getDb();
|
|
40
|
+
const sourceRows = sourceId
|
|
41
|
+
? await db
|
|
42
|
+
.select()
|
|
43
|
+
.from(schema.contentDatabaseSourceRows)
|
|
44
|
+
.where(eq(schema.contentDatabaseSourceRows.sourceId, sourceId))
|
|
45
|
+
: [];
|
|
46
|
+
const sourceDocumentIds = sourceRows.map((row) => row.documentId);
|
|
47
|
+
const rows = await db
|
|
32
48
|
.select()
|
|
33
49
|
.from(schema.documents)
|
|
34
|
-
.where(
|
|
50
|
+
.where(
|
|
51
|
+
sourceId
|
|
52
|
+
? sourceDocumentIds.length
|
|
53
|
+
? and(
|
|
54
|
+
accessFilter(schema.documents, schema.documentShares),
|
|
55
|
+
inArray(schema.documents.id, sourceDocumentIds),
|
|
56
|
+
)
|
|
57
|
+
: eq(schema.documents.id, "__no_local_folder_documents__")
|
|
58
|
+
: accessFilter(schema.documents, schema.documentShares),
|
|
59
|
+
)
|
|
35
60
|
.orderBy(asc(schema.documents.position), asc(schema.documents.title));
|
|
36
61
|
|
|
37
62
|
const documents: ContentSourceDocument[] = rows.map((doc) => ({
|
|
@@ -48,9 +73,22 @@ export default defineAction({
|
|
|
48
73
|
updatedAt: doc.updatedAt,
|
|
49
74
|
}));
|
|
50
75
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
if (sourceId) {
|
|
77
|
+
const pathByDocumentId = new Map(
|
|
78
|
+
sourceRows.map((row) => [row.documentId, row.sourceDisplayKey]),
|
|
79
|
+
);
|
|
80
|
+
return {
|
|
81
|
+
root: "",
|
|
82
|
+
exportedAt: new Date().toISOString(),
|
|
83
|
+
files: Object.fromEntries(
|
|
84
|
+
documents.map((document) => [
|
|
85
|
+
pathByDocumentId.get(document.id) ?? `content/${document.id}.mdx`,
|
|
86
|
+
serializeContentSourceDocument(document),
|
|
87
|
+
]),
|
|
88
|
+
),
|
|
89
|
+
count: documents.length,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return { ...buildContentSourceBundle(documents), count: documents.length };
|
|
55
93
|
},
|
|
56
94
|
});
|
|
@@ -15,12 +15,6 @@ import {
|
|
|
15
15
|
serializeDatabaseMembership,
|
|
16
16
|
} from "./_database-utils.js";
|
|
17
17
|
import { serializeDocumentSource } from "./_document-source.js";
|
|
18
|
-
import {
|
|
19
|
-
getLocalFileDocument,
|
|
20
|
-
getLocalDocumentContextPath,
|
|
21
|
-
isLocalDocumentId,
|
|
22
|
-
isContentLocalFileMode,
|
|
23
|
-
} from "./_local-file-documents.js";
|
|
24
18
|
import "../server/db/index.js";
|
|
25
19
|
import {
|
|
26
20
|
listPropertiesForDocument,
|
|
@@ -46,14 +40,6 @@ export default defineAction({
|
|
|
46
40
|
run: async (args) => {
|
|
47
41
|
if (!args.id) throw new Error("--id is required");
|
|
48
42
|
|
|
49
|
-
if ((await isContentLocalFileMode()) && isLocalDocumentId(args.id)) {
|
|
50
|
-
const document = await getLocalFileDocument(args.id);
|
|
51
|
-
return {
|
|
52
|
-
...document,
|
|
53
|
-
contextPath: await getLocalDocumentContextPath(args.id),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
43
|
const access = await resolveAccess("document", args.id);
|
|
58
44
|
// Not-found is a deterministic client-state condition (deleted or
|
|
59
45
|
// inaccessible document still referenced by an open tab) — 404, not a
|
|
@@ -15,6 +15,13 @@ import {
|
|
|
15
15
|
parseContentSourceFile,
|
|
16
16
|
type ParsedContentSourceFile,
|
|
17
17
|
} from "../shared/content-source.js";
|
|
18
|
+
import { ensureDocumentsFilesMembership } from "./_content-files.js";
|
|
19
|
+
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
20
|
+
import {
|
|
21
|
+
organizationContentSpaceId,
|
|
22
|
+
personalContentSpaceId,
|
|
23
|
+
provisionContentSpaces,
|
|
24
|
+
} from "./_content-spaces.js";
|
|
18
25
|
|
|
19
26
|
const MAX_SOURCE_FILES = 500;
|
|
20
27
|
const MAX_SOURCE_FILE_BYTES = 2 * 1024 * 1024;
|
|
@@ -187,6 +194,18 @@ export default defineAction({
|
|
|
187
194
|
const currentOrgId = getRequestOrgId() ?? null;
|
|
188
195
|
const db = getDb();
|
|
189
196
|
const now = new Date().toISOString();
|
|
197
|
+
const defaultSpaceId = currentOrgId
|
|
198
|
+
? organizationContentSpaceId(currentOrgId)
|
|
199
|
+
: personalContentSpaceId(currentUserEmail);
|
|
200
|
+
if (!dryRun) {
|
|
201
|
+
const provisioned = await provisionContentSpaces(db, currentUserEmail);
|
|
202
|
+
if (!provisioned.spaceIds.includes(defaultSpaceId)) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
"The active organization does not have a writable Content space.",
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
await resolveContentSpaceAccess(defaultSpaceId, "editor");
|
|
208
|
+
}
|
|
190
209
|
const parsed: ParsedContentSourceFile[] = entries.map(
|
|
191
210
|
([filePath, source]) => parseContentSourceFile(filePath, source),
|
|
192
211
|
);
|
|
@@ -229,6 +248,7 @@ export default defineAction({
|
|
|
229
248
|
const idByPath = new Map<string, string>();
|
|
230
249
|
const pathById = new Map<string, string>();
|
|
231
250
|
const ownerById = new Map<string, string>();
|
|
251
|
+
const spaceById = new Map<string, string>();
|
|
232
252
|
const desiredParentById = new Map<string, string | null>();
|
|
233
253
|
const desiredPositionById = new Map<string, number>();
|
|
234
254
|
const currentParentById = new Map<string, string | null>();
|
|
@@ -271,7 +291,9 @@ export default defineAction({
|
|
|
271
291
|
}
|
|
272
292
|
|
|
273
293
|
if (existing) {
|
|
294
|
+
const existingSpaceId = existing.spaceId ?? defaultSpaceId;
|
|
274
295
|
ownerById.set(id, existing.ownerEmail as string);
|
|
296
|
+
spaceById.set(id, existingSpaceId);
|
|
275
297
|
currentParentById.set(id, existing.parentId ?? null);
|
|
276
298
|
currentPositionById.set(id, existing.position ?? 0);
|
|
277
299
|
if (file.parentId !== undefined) {
|
|
@@ -299,6 +321,7 @@ export default defineAction({
|
|
|
299
321
|
existing.sourceKind !== sourceUpdates.sourceKind ||
|
|
300
322
|
existing.sourcePath !== sourceUpdates.sourcePath ||
|
|
301
323
|
existing.sourceRootPath !== sourceUpdates.sourceRootPath;
|
|
324
|
+
const spaceChanged = !existing.spaceId;
|
|
302
325
|
const anyChange =
|
|
303
326
|
titleChanged ||
|
|
304
327
|
contentChanged ||
|
|
@@ -306,7 +329,8 @@ export default defineAction({
|
|
|
306
329
|
iconChanged ||
|
|
307
330
|
favoriteChanged ||
|
|
308
331
|
discoverabilityChanged ||
|
|
309
|
-
sourceChanged
|
|
332
|
+
sourceChanged ||
|
|
333
|
+
spaceChanged;
|
|
310
334
|
|
|
311
335
|
if (!anyChange) {
|
|
312
336
|
unchanged.push({ id, path: file.path, title: existing.title });
|
|
@@ -324,6 +348,7 @@ export default defineAction({
|
|
|
324
348
|
}
|
|
325
349
|
|
|
326
350
|
const updates: Record<string, unknown> = { updatedAt: now };
|
|
351
|
+
if (!existing.spaceId) updates.spaceId = existingSpaceId;
|
|
327
352
|
if (titleChanged) updates.title = file.title;
|
|
328
353
|
if (contentChanged) updates.content = file.content;
|
|
329
354
|
if (descriptionChanged) updates.description = file.description;
|
|
@@ -348,6 +373,7 @@ export default defineAction({
|
|
|
348
373
|
try {
|
|
349
374
|
await db.insert(schema.documents).values({
|
|
350
375
|
id,
|
|
376
|
+
spaceId: defaultSpaceId,
|
|
351
377
|
ownerEmail: currentUserEmail,
|
|
352
378
|
orgId: currentOrgId,
|
|
353
379
|
parentId: null,
|
|
@@ -382,6 +408,7 @@ export default defineAction({
|
|
|
382
408
|
desiredParentById.set(id, file.parentId ?? null);
|
|
383
409
|
desiredPositionById.set(id, file.position ?? index);
|
|
384
410
|
ownerById.set(id, currentUserEmail);
|
|
411
|
+
spaceById.set(id, defaultSpaceId);
|
|
385
412
|
created.push({ id, path: file.path, title: file.title });
|
|
386
413
|
}
|
|
387
414
|
|
|
@@ -410,7 +437,8 @@ export default defineAction({
|
|
|
410
437
|
const parentAccess = await resolveAccess("document", parentId);
|
|
411
438
|
if (
|
|
412
439
|
parentAccess &&
|
|
413
|
-
(parentAccess.resource.ownerEmail as string) === ownerEmail
|
|
440
|
+
(parentAccess.resource.ownerEmail as string) === ownerEmail &&
|
|
441
|
+
parentAccess.resource.spaceId === spaceById.get(id)
|
|
414
442
|
) {
|
|
415
443
|
try {
|
|
416
444
|
await assertParentIsNotDescendant({ ownerEmail, id, parentId });
|
|
@@ -428,7 +456,7 @@ export default defineAction({
|
|
|
428
456
|
} else {
|
|
429
457
|
skipped.push({
|
|
430
458
|
path: pathById.get(id) ?? id,
|
|
431
|
-
reason: `Skipped parent "${parentId}" because it is not editable in the same
|
|
459
|
+
reason: `Skipped parent "${parentId}" because it is not editable in the same Content space.`,
|
|
432
460
|
});
|
|
433
461
|
}
|
|
434
462
|
}
|
|
@@ -452,6 +480,18 @@ export default defineAction({
|
|
|
452
480
|
.where(eq(schema.documents.id, id));
|
|
453
481
|
}
|
|
454
482
|
|
|
483
|
+
await ensureDocumentsFilesMembership(
|
|
484
|
+
db,
|
|
485
|
+
[
|
|
486
|
+
...new Set([
|
|
487
|
+
...created.map((row) => row.id),
|
|
488
|
+
...updated.map((row) => row.id),
|
|
489
|
+
...unchanged.map((row) => row.id),
|
|
490
|
+
]),
|
|
491
|
+
],
|
|
492
|
+
now,
|
|
493
|
+
);
|
|
494
|
+
|
|
455
495
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
456
496
|
}
|
|
457
497
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
3
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import {
|
|
8
|
+
listContentOrganizationMemberships,
|
|
9
|
+
normalizeContentSpaceEmail,
|
|
10
|
+
} from "./_content-space-access.js";
|
|
11
|
+
import {
|
|
12
|
+
personalContentSpaceId,
|
|
13
|
+
systemIdsForContentSpace,
|
|
14
|
+
} from "./_content-spaces.js";
|
|
15
|
+
|
|
16
|
+
export default defineAction({
|
|
17
|
+
description:
|
|
18
|
+
"List Content spaces that are already provisioned and currently authorized for the signed-in user.",
|
|
19
|
+
schema: z.object({}),
|
|
20
|
+
http: { method: "GET" },
|
|
21
|
+
readOnly: true,
|
|
22
|
+
run: async () => {
|
|
23
|
+
const userEmail = getRequestUserEmail();
|
|
24
|
+
if (!userEmail) throw new Error("no authenticated user");
|
|
25
|
+
const email = normalizeContentSpaceEmail(userEmail);
|
|
26
|
+
const db = getDb();
|
|
27
|
+
const personalSpaceId = personalContentSpaceId(email);
|
|
28
|
+
const catalogIds = systemIdsForContentSpace(personalSpaceId, "workspaces");
|
|
29
|
+
const memberships = await listContentOrganizationMemberships(email);
|
|
30
|
+
const roleByOrgId = new Map(
|
|
31
|
+
memberships.map((membership) => [
|
|
32
|
+
membership.orgId,
|
|
33
|
+
membership.role === "owner"
|
|
34
|
+
? "owner"
|
|
35
|
+
: membership.role === "admin"
|
|
36
|
+
? "editor"
|
|
37
|
+
: "viewer",
|
|
38
|
+
]),
|
|
39
|
+
);
|
|
40
|
+
const rows = await db
|
|
41
|
+
.select({
|
|
42
|
+
mapping: schema.contentSpaceCatalogItems,
|
|
43
|
+
space: schema.contentSpaces,
|
|
44
|
+
item: schema.contentDatabaseItems,
|
|
45
|
+
})
|
|
46
|
+
.from(schema.contentSpaceCatalogItems)
|
|
47
|
+
.innerJoin(
|
|
48
|
+
schema.contentSpaces,
|
|
49
|
+
eq(schema.contentSpaces.id, schema.contentSpaceCatalogItems.spaceId),
|
|
50
|
+
)
|
|
51
|
+
.innerJoin(
|
|
52
|
+
schema.contentDatabaseItems,
|
|
53
|
+
eq(
|
|
54
|
+
schema.contentDatabaseItems.id,
|
|
55
|
+
schema.contentSpaceCatalogItems.databaseItemId,
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
.where(
|
|
59
|
+
and(
|
|
60
|
+
eq(schema.contentSpaceCatalogItems.ownerEmail, email),
|
|
61
|
+
eq(
|
|
62
|
+
schema.contentSpaceCatalogItems.catalogDatabaseId,
|
|
63
|
+
catalogIds.databaseId,
|
|
64
|
+
),
|
|
65
|
+
isNull(schema.contentSpaces.archivedAt),
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
const spaces = [] as Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
kind: string;
|
|
72
|
+
filesDatabaseId: string;
|
|
73
|
+
orgId: string | null;
|
|
74
|
+
role: string;
|
|
75
|
+
catalogItemId: string;
|
|
76
|
+
catalogDocumentId: string;
|
|
77
|
+
}>;
|
|
78
|
+
for (const row of rows) {
|
|
79
|
+
if (
|
|
80
|
+
row.item.databaseId !== catalogIds.databaseId ||
|
|
81
|
+
row.item.documentId !== row.mapping.documentId
|
|
82
|
+
)
|
|
83
|
+
continue;
|
|
84
|
+
const role = row.space.orgId
|
|
85
|
+
? roleByOrgId.get(row.space.orgId)
|
|
86
|
+
: row.space.ownerEmail.toLowerCase() === email
|
|
87
|
+
? "owner"
|
|
88
|
+
: undefined;
|
|
89
|
+
if (!role) continue;
|
|
90
|
+
spaces.push({
|
|
91
|
+
id: row.space.id,
|
|
92
|
+
name: row.space.name,
|
|
93
|
+
kind: row.space.kind,
|
|
94
|
+
filesDatabaseId: row.space.filesDatabaseId,
|
|
95
|
+
orgId: row.space.orgId,
|
|
96
|
+
role,
|
|
97
|
+
catalogItemId: row.mapping.databaseItemId,
|
|
98
|
+
catalogDocumentId: row.mapping.documentId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return { catalogDatabaseId: catalogIds.databaseId, spaces };
|
|
102
|
+
},
|
|
103
|
+
});
|