@agent-native/core 0.114.0 → 0.114.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 +2 -2
- package/corpus/core/CHANGELOG.md +9 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/build-compatibility.ts +77 -0
- package/corpus/core/src/client/route-chunk-recovery/index.ts +1 -0
- package/corpus/core/src/client/use-action.ts +32 -1
- package/corpus/core/src/deploy/build.ts +4 -1
- package/corpus/core/src/org/context.ts +56 -30
- package/corpus/core/src/server/action-routes.ts +52 -2
- package/corpus/core/src/server/auth-plugin.ts +9 -4
- package/corpus/core/src/server/better-auth-instance.ts +5 -1
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/vite/client.ts +17 -0
- package/corpus/templates/content/AGENTS.md +2 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +49 -5
- package/corpus/templates/content/actions/_content-document-access.ts +21 -0
- package/corpus/templates/content/actions/_content-favorites.ts +91 -0
- package/corpus/templates/content/actions/_content-files.ts +79 -23
- package/corpus/templates/content/actions/_content-sidebar-state.ts +24 -0
- package/corpus/templates/content/actions/_content-space-access.ts +15 -17
- package/corpus/templates/content/actions/_content-space-catalog-guards.ts +20 -0
- package/corpus/templates/content/actions/_content-spaces.ts +364 -167
- package/corpus/templates/content/actions/_database-source-utils.ts +8 -0
- package/corpus/templates/content/actions/_database-utils.ts +249 -15
- package/corpus/templates/content/actions/_delete-content-space.ts +84 -0
- package/corpus/templates/content/actions/_files-system-properties.ts +499 -0
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -1
- package/corpus/templates/content/actions/_property-utils.ts +18 -9
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +3 -0
- package/corpus/templates/content/actions/configure-document-property.ts +3 -0
- package/corpus/templates/content/actions/create-content-database.ts +2 -2
- package/corpus/templates/content/actions/create-content-space.ts +34 -0
- package/corpus/templates/content/actions/create-document.ts +4 -1
- package/corpus/templates/content/actions/delete-content-space.ts +19 -0
- package/corpus/templates/content/actions/delete-database-items.ts +28 -1
- package/corpus/templates/content/actions/delete-document-property.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +39 -2
- package/corpus/templates/content/actions/duplicate-database-item.ts +6 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +6 -0
- package/corpus/templates/content/actions/duplicate-document-property.ts +3 -0
- package/corpus/templates/content/actions/get-content-database.ts +11 -2
- package/corpus/templates/content/actions/get-content-sidebar-state.ts +23 -0
- package/corpus/templates/content/actions/get-document.ts +32 -7
- package/corpus/templates/content/actions/import-content-source.ts +33 -1
- package/corpus/templates/content/actions/list-content-spaces.ts +34 -2
- package/corpus/templates/content/actions/list-documents.ts +39 -7
- package/corpus/templates/content/actions/reorder-document-property.ts +9 -0
- package/corpus/templates/content/actions/set-document-property.ts +41 -23
- package/corpus/templates/content/actions/share-local-file-document.ts +15 -0
- package/corpus/templates/content/actions/submit-content-database-form.ts +7 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +14 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +1 -0
- package/corpus/templates/content/actions/update-content-sidebar-state.ts +27 -0
- package/corpus/templates/content/actions/update-document.ts +158 -66
- package/corpus/templates/content/app/components/EmptyState.tsx +4 -57
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +8 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +519 -178
- package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +39 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +266 -7
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +430 -152
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +467 -66
- package/corpus/templates/content/app/components/editor/database/view-config.ts +9 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +720 -196
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +173 -1
- package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +1 -1
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +91 -16
- package/corpus/templates/content/app/entry.client.tsx +5 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +84 -3
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +60 -2
- package/corpus/templates/content/app/hooks/use-create-page.ts +30 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +270 -19
- package/corpus/templates/content/app/i18n-data.ts +243 -20
- package/corpus/templates/content/app/lib/local-folder-picker-safety.ts +54 -0
- package/corpus/templates/content/app/lib/local-folder-picker-support.ts +18 -0
- package/corpus/templates/content/app/routes/_app.favorites.tsx +30 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +18 -6
- package/corpus/templates/content/changelog/2026-07-17-workspace-files-now-stay-in-a-loading-state-until-automatic-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspace-switching-now-opens-the-selected-files-database-pr.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspaces-now-expand-above-their-own-files-list-without-dup.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-create-named-workspaces-each-with-its-own-private-files-data.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-favorites-align-with-workspace-and-file-rows-in-the-sidebar.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-local-folder-selection-no-longer-invokes-the-unsafe-native-p.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-made-content-workspaces-independently-expandable-restored-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-page-details-now-live-in-a-shared-info-and-comments-rail-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-database-views-now-control-sidebar-workspace-navig.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-navigation-now-aligns-favorites-with-workspace-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-favorites-now-open-as-a-table-and-compact-breadcrumbs-make-w.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-files-databases-now-start-unfiltered-sidebar-rows-exactly-fo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-sidebar-page-trees-now-open-only-when-expanded-remember-thei.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-file-databases-now-use-the-workspace-name-open-as-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-navigation-now-stays-stable-on-hover-uses-folder-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-rows-now-use-folder-icons-and-deleting-a-user-crea.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspaces-can-be-created-without-leaving-the-workspaces-dat.md +6 -0
- package/corpus/templates/content/e2e/global-setup.ts +3 -2
- package/corpus/templates/content/parity/matrix.md +28 -28
- package/corpus/templates/content/parity/matrix.ts +8 -2
- package/corpus/templates/content/server/db/index.ts +1 -0
- package/corpus/templates/content/server/db/schema.ts +10 -0
- package/corpus/templates/content/server/lib/documents.ts +12 -7
- package/corpus/templates/content/server/plugins/db.ts +17 -0
- package/corpus/templates/content/shared/api.ts +9 -0
- package/corpus/templates/content/shared/database-form.ts +11 -2
- package/corpus/templates/content/vite.config.ts +1 -0
- package/dist/client/build-compatibility.d.ts +11 -0
- package/dist/client/build-compatibility.d.ts.map +1 -0
- package/dist/client/build-compatibility.js +56 -0
- package/dist/client/build-compatibility.js.map +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery/index.js +1 -0
- package/dist/client/route-chunk-recovery/index.js.map +1 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +19 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +2 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/context.d.ts +4 -2
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +44 -22
- package/dist/org/context.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +32 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth-plugin.d.ts.map +1 -1
- package/dist/server/auth-plugin.js +9 -5
- package/dist/server/auth-plugin.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +4 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/vite/client.d.ts +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +8 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/build-compatibility.ts +77 -0
- package/src/client/route-chunk-recovery/index.ts +1 -0
- package/src/client/use-action.ts +32 -1
- package/src/deploy/build.ts +4 -1
- package/src/org/context.ts +56 -30
- package/src/server/action-routes.ts +52 -2
- package/src/server/auth-plugin.ts +9 -4
- package/src/server/better-auth-instance.ts +5 -1
- package/src/shared/mcp-embed-headers.ts +1 -1
- package/src/vite/client.ts +17 -0
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.114.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c913280: Keep action mutations pending until asynchronous success callbacks finish.
|
|
8
|
+
- c913280: Allow email-password signup without email verification on Netlify deploy previews while preserving explicit verification overrides, and hold auth requests until their serverless routes finish mounting.
|
|
9
|
+
- c913280: Fence incompatible cached browser clients from newer action backends and reload version-aware tabs with a cache-busted build.
|
|
10
|
+
- c913280: Honor an explicit Personal context for users who also belong to organizations.
|
|
11
|
+
|
|
3
12
|
## 0.114.0
|
|
4
13
|
|
|
5
14
|
### Minor Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.114.
|
|
3
|
+
"version": "0.114.1",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
declare const __AGENT_NATIVE_BUILD_ID__: string | undefined;
|
|
2
|
+
declare const __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__: string | undefined;
|
|
3
|
+
|
|
4
|
+
const RELOAD_MARKER_KEY = "__agentNativeClientCompatibilityReload";
|
|
5
|
+
export const BUILD_CACHE_BUSTER_PARAM = "__an_build";
|
|
6
|
+
|
|
7
|
+
type CompatibilityWindow = Pick<Window, "location" | "history"> & {
|
|
8
|
+
sessionStorage?: Storage;
|
|
9
|
+
__agentNativeClientCompatibilityReload?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function clientBuildId(): string {
|
|
13
|
+
if (typeof __AGENT_NATIVE_BUILD_ID__ === "string") {
|
|
14
|
+
return __AGENT_NATIVE_BUILD_ID__;
|
|
15
|
+
}
|
|
16
|
+
return (
|
|
17
|
+
(globalThis as typeof globalThis & { __AGENT_NATIVE_BUILD_ID__?: string })
|
|
18
|
+
.__AGENT_NATIVE_BUILD_ID__ ?? ""
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function clientCompatibilityVersion(): string {
|
|
23
|
+
if (typeof __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__ === "string") {
|
|
24
|
+
return __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__;
|
|
25
|
+
}
|
|
26
|
+
return (
|
|
27
|
+
(
|
|
28
|
+
globalThis as typeof globalThis & {
|
|
29
|
+
__AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__?: string;
|
|
30
|
+
}
|
|
31
|
+
).__AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__ ?? ""
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readReloadMarker(win: CompatibilityWindow): string {
|
|
36
|
+
try {
|
|
37
|
+
return win.sessionStorage?.getItem(RELOAD_MARKER_KEY) ?? "";
|
|
38
|
+
} catch {
|
|
39
|
+
return win.__agentNativeClientCompatibilityReload ?? "";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function writeReloadMarker(win: CompatibilityWindow, marker: string): void {
|
|
44
|
+
win.__agentNativeClientCompatibilityReload = marker;
|
|
45
|
+
try {
|
|
46
|
+
win.sessionStorage?.setItem(RELOAD_MARKER_KEY, marker);
|
|
47
|
+
} catch {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function reloadForClientCompatibilityMismatch(
|
|
51
|
+
serverBuildId: string,
|
|
52
|
+
requiredCompatibility: string,
|
|
53
|
+
win: CompatibilityWindow | undefined = typeof window === "undefined"
|
|
54
|
+
? undefined
|
|
55
|
+
: window,
|
|
56
|
+
): boolean {
|
|
57
|
+
if (!win?.location) return false;
|
|
58
|
+
const marker = `${requiredCompatibility}:${serverBuildId}`;
|
|
59
|
+
if (readReloadMarker(win) === marker) return false;
|
|
60
|
+
writeReloadMarker(win, marker);
|
|
61
|
+
const target = new URL(win.location.href);
|
|
62
|
+
target.searchParams.set(BUILD_CACHE_BUSTER_PARAM, serverBuildId || "latest");
|
|
63
|
+
win.location.replace(target.href);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function stripBuildCompatibilityCacheBuster(
|
|
68
|
+
win: CompatibilityWindow | undefined = typeof window === "undefined"
|
|
69
|
+
? undefined
|
|
70
|
+
: window,
|
|
71
|
+
): void {
|
|
72
|
+
if (!win?.location || !win.history) return;
|
|
73
|
+
const target = new URL(win.location.href);
|
|
74
|
+
if (!target.searchParams.has(BUILD_CACHE_BUSTER_PARAM)) return;
|
|
75
|
+
target.searchParams.delete(BUILD_CACHE_BUSTER_PARAM);
|
|
76
|
+
win.history.replaceState(win.history.state, "", target.href);
|
|
77
|
+
}
|
|
@@ -30,6 +30,11 @@ import type {
|
|
|
30
30
|
import { trackEvent } from "./analytics.js";
|
|
31
31
|
import { agentNativePath } from "./api-path.js";
|
|
32
32
|
import { getBrowserTabId } from "./browser-tab-id.js";
|
|
33
|
+
import {
|
|
34
|
+
clientBuildId,
|
|
35
|
+
clientCompatibilityVersion,
|
|
36
|
+
reloadForClientCompatibilityMismatch,
|
|
37
|
+
} from "./build-compatibility.js";
|
|
33
38
|
import { ensureEmbedAuthFetchInterceptor } from "./embed-auth.js";
|
|
34
39
|
|
|
35
40
|
const ACTION_PREFIX = agentNativePath("/_agent-native/actions");
|
|
@@ -281,6 +286,12 @@ async function performActionFetch<T>(
|
|
|
281
286
|
}
|
|
282
287
|
: {}),
|
|
283
288
|
};
|
|
289
|
+
const compatibilityVersion = clientCompatibilityVersion();
|
|
290
|
+
if (compatibilityVersion) {
|
|
291
|
+
headers["X-Agent-Native-Client-Compatibility"] = compatibilityVersion;
|
|
292
|
+
}
|
|
293
|
+
const buildId = clientBuildId();
|
|
294
|
+
if (buildId) headers["X-Agent-Native-Build-Id"] = buildId;
|
|
284
295
|
const tz = resolveUserTimezone();
|
|
285
296
|
if (tz) headers["x-user-timezone"] = tz;
|
|
286
297
|
const init: RequestInit = {
|
|
@@ -349,6 +360,26 @@ async function performActionFetch<T>(
|
|
|
349
360
|
throw new Error(`Action ${name} failed: ${cause}`);
|
|
350
361
|
}
|
|
351
362
|
|
|
363
|
+
if (
|
|
364
|
+
res.status === 409 &&
|
|
365
|
+
res.headers.get("X-Agent-Native-Client-Mismatch") === "1"
|
|
366
|
+
) {
|
|
367
|
+
const serverBuildId =
|
|
368
|
+
res.headers.get("X-Agent-Native-Build-Id") ?? "latest";
|
|
369
|
+
const requiredCompatibility =
|
|
370
|
+
res.headers.get("X-Agent-Native-Client-Compatibility") ?? "unknown";
|
|
371
|
+
reloadForClientCompatibilityMismatch(
|
|
372
|
+
serverBuildId,
|
|
373
|
+
requiredCompatibility,
|
|
374
|
+
);
|
|
375
|
+
const error = new Error(
|
|
376
|
+
`Action ${name} requires a refreshed browser client`,
|
|
377
|
+
);
|
|
378
|
+
(error as any).status = 409;
|
|
379
|
+
(error as any).code = "client_build_mismatch";
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
|
|
352
383
|
// 204 No Content — nothing to parse.
|
|
353
384
|
if (res.status === 204) return null as T;
|
|
354
385
|
|
|
@@ -760,7 +791,7 @@ export function useActionMutation<
|
|
|
760
791
|
if (!skipActionQueryInvalidation) {
|
|
761
792
|
queryClient.invalidateQueries({ queryKey: ["action"] });
|
|
762
793
|
}
|
|
763
|
-
(onSuccess as Function)?.(...args);
|
|
794
|
+
return (onSuccess as Function)?.(...args);
|
|
764
795
|
},
|
|
765
796
|
});
|
|
766
797
|
}
|
|
@@ -1240,7 +1240,7 @@ async function getHandler() {
|
|
|
1240
1240
|
headers: {
|
|
1241
1241
|
"Access-Control-Allow-Origin": "*",
|
|
1242
1242
|
"Access-Control-Allow-Methods": "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
1243
|
-
"Access-Control-Allow-Headers": "Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Embed-Target",
|
|
1243
|
+
"Access-Control-Allow-Headers": "Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,X-Agent-Native-Embed-Target",
|
|
1244
1244
|
},
|
|
1245
1245
|
});
|
|
1246
1246
|
}
|
|
@@ -3479,6 +3479,9 @@ export default bundle;
|
|
|
3479
3479
|
"process.env.AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID": JSON.stringify(
|
|
3480
3480
|
process.env.GA_MEASUREMENT_ID?.trim() || "",
|
|
3481
3481
|
),
|
|
3482
|
+
"process.env.AGENT_NATIVE_BUILD_DEPLOY_CONTEXT": JSON.stringify(
|
|
3483
|
+
process.env.CONTEXT?.trim() || "",
|
|
3484
|
+
),
|
|
3482
3485
|
},
|
|
3483
3486
|
// Replace browser-only renderers (Excalidraw/Mermaid) with an inert proxy in
|
|
3484
3487
|
// the server bundle. Without this, Nitro's Rolldown build pulls the real
|
|
@@ -41,7 +41,8 @@ const nanoid = (): string =>
|
|
|
41
41
|
/**
|
|
42
42
|
* Resolve the current user's organization context from their session.
|
|
43
43
|
*
|
|
44
|
-
* -
|
|
44
|
+
* - Honors the user's `active-org-id` setting, including an explicit Personal
|
|
45
|
+
* context represented by `{ orgId: null }`.
|
|
45
46
|
* - Falls back to the user's first membership.
|
|
46
47
|
* - When the authenticated user has zero memberships, provisions a default org
|
|
47
48
|
* named after the user ({name}'s workspace, falling back to the email
|
|
@@ -63,6 +64,34 @@ export async function getOrgContext(event: H3Event): Promise<OrgContext> {
|
|
|
63
64
|
type MembershipRow = { orgId: string; role: OrgRole; orgName: string };
|
|
64
65
|
|
|
65
66
|
const MEMBERSHIPS_CACHE_KEY = "__anOrgMembershipsCache";
|
|
67
|
+
const ACTIVE_ORG_SETTING_CACHE_KEY = "__anActiveOrgSettingCache";
|
|
68
|
+
|
|
69
|
+
type ActiveOrgSetting = { orgId: string | null } | null;
|
|
70
|
+
|
|
71
|
+
function loadActiveOrgSettingForEvent(
|
|
72
|
+
event: H3Event,
|
|
73
|
+
email: string,
|
|
74
|
+
): Promise<ActiveOrgSetting> {
|
|
75
|
+
const ctx = event.context as Record<string, unknown>;
|
|
76
|
+
const cache = ((ctx[ACTIVE_ORG_SETTING_CACHE_KEY] as
|
|
77
|
+
| Map<string, Promise<ActiveOrgSetting>>
|
|
78
|
+
| undefined) ??
|
|
79
|
+
(ctx[ACTIVE_ORG_SETTING_CACHE_KEY] = new Map<
|
|
80
|
+
string,
|
|
81
|
+
Promise<ActiveOrgSetting>
|
|
82
|
+
>())) as Map<string, Promise<ActiveOrgSetting>>;
|
|
83
|
+
const normalizedEmail = email.toLowerCase();
|
|
84
|
+
let promise = cache.get(normalizedEmail);
|
|
85
|
+
if (!promise) {
|
|
86
|
+
promise = getUserSetting(email, "active-org-id").then((value) => {
|
|
87
|
+
if (!value || !("orgId" in value)) return null;
|
|
88
|
+
if (value.orgId === null) return { orgId: null };
|
|
89
|
+
return typeof value.orgId === "string" ? { orgId: value.orgId } : null;
|
|
90
|
+
});
|
|
91
|
+
cache.set(normalizedEmail, promise);
|
|
92
|
+
}
|
|
93
|
+
return promise;
|
|
94
|
+
}
|
|
66
95
|
|
|
67
96
|
/**
|
|
68
97
|
* Per-request memoization of the org_members lookup, keyed by email on
|
|
@@ -128,32 +157,29 @@ async function resolveOrgContextUncached(event: H3Event): Promise<OrgContext> {
|
|
|
128
157
|
return { email, orgId: null, orgName: null, role: null };
|
|
129
158
|
}
|
|
130
159
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
const shouldTryDomainAutoJoin =
|
|
161
|
+
memberships.length === 0 ||
|
|
162
|
+
(memberships.length === 1 &&
|
|
163
|
+
isLikelyPersonalWorkspace(memberships[0], email, session));
|
|
164
|
+
const activeOrgSetting = await loadActiveOrgSettingForEvent(event, email);
|
|
165
|
+
if (activeOrgSetting?.orgId === null) {
|
|
166
|
+
return { email, orgId: null, orgName: null, role: null };
|
|
167
|
+
}
|
|
168
|
+
if (activeOrgSetting?.orgId) {
|
|
169
|
+
const active = memberships.find((m) => m.orgId === activeOrgSetting.orgId);
|
|
170
|
+
if (active && !shouldTryDomainAutoJoin) {
|
|
171
|
+
return {
|
|
172
|
+
email,
|
|
173
|
+
orgId: active.orgId,
|
|
174
|
+
orgName: active.orgName,
|
|
175
|
+
role: active.role,
|
|
176
|
+
};
|
|
147
177
|
}
|
|
148
178
|
}
|
|
149
179
|
|
|
150
180
|
const sessionMembership = sessionOrgId
|
|
151
181
|
? memberships.find((m) => m.orgId === sessionOrgId)
|
|
152
182
|
: null;
|
|
153
|
-
const shouldTryDomainAutoJoin =
|
|
154
|
-
memberships.length === 0 ||
|
|
155
|
-
(memberships.length === 1 &&
|
|
156
|
-
isLikelyPersonalWorkspace(memberships[0], email, session));
|
|
157
183
|
|
|
158
184
|
if (shouldTryDomainAutoJoin) {
|
|
159
185
|
const joined = await autoJoinDomainMatchingOrgs(email, {
|
|
@@ -248,7 +274,8 @@ async function loadMemberships(
|
|
|
248
274
|
/**
|
|
249
275
|
* Resolve the active org ID for a given email — for non-HTTP contexts like
|
|
250
276
|
* the integration webhook handler where we have an email but no event/session.
|
|
251
|
-
* Picks the user's active-org-id setting if set,
|
|
277
|
+
* Picks the user's active-org-id setting if set, including explicit Personal,
|
|
278
|
+
* otherwise the first membership.
|
|
252
279
|
* Returns null if the user has no memberships.
|
|
253
280
|
*/
|
|
254
281
|
export async function resolveOrgIdForEmail(
|
|
@@ -263,10 +290,11 @@ export async function resolveOrgIdForEmail(
|
|
|
263
290
|
});
|
|
264
291
|
if (rows.length === 0) return null;
|
|
265
292
|
const ids = rows.map((r: any) => String(r.org_id));
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
const activeOrgSetting = (await getUserSetting(
|
|
294
|
+
email,
|
|
295
|
+
"active-org-id",
|
|
296
|
+
)) as ActiveOrgSetting;
|
|
297
|
+
if (activeOrgSetting?.orgId === null) return null;
|
|
270
298
|
if (activeOrgSetting?.orgId && ids.includes(activeOrgSetting.orgId)) {
|
|
271
299
|
return activeOrgSetting.orgId;
|
|
272
300
|
}
|
|
@@ -291,10 +319,8 @@ export async function resolveOrgIdForEmailViaEvent(
|
|
|
291
319
|
if (!exec) return null;
|
|
292
320
|
const memberships = await loadMembershipsForEvent(event, exec, email);
|
|
293
321
|
if (!memberships || memberships.length === 0) return null;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
orgId: string;
|
|
297
|
-
} | null;
|
|
322
|
+
const activeOrgSetting = await loadActiveOrgSettingForEvent(event, email);
|
|
323
|
+
if (activeOrgSetting?.orgId === null) return null;
|
|
298
324
|
if (
|
|
299
325
|
activeOrgSetting?.orgId &&
|
|
300
326
|
memberships.some((m) => m.orgId === activeOrgSetting.orgId)
|
|
@@ -30,6 +30,26 @@ import {
|
|
|
30
30
|
getAllowedCorsOrigin as resolveAllowedCorsOrigin,
|
|
31
31
|
readCorsAllowedOrigins,
|
|
32
32
|
} from "./cors-origins.js";
|
|
33
|
+
|
|
34
|
+
declare const __AGENT_NATIVE_BUILD_ID__: string | undefined;
|
|
35
|
+
declare const __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__: string | undefined;
|
|
36
|
+
|
|
37
|
+
function requiredClientCompatibilityVersion(): string {
|
|
38
|
+
const configured =
|
|
39
|
+
typeof __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__ === "string"
|
|
40
|
+
? __AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__
|
|
41
|
+
: process.env.AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION;
|
|
42
|
+
return configured?.trim() ?? "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function currentBuildId(): string {
|
|
46
|
+
const configured =
|
|
47
|
+
typeof __AGENT_NATIVE_BUILD_ID__ === "string"
|
|
48
|
+
? __AGENT_NATIVE_BUILD_ID__
|
|
49
|
+
: process.env.AGENT_NATIVE_BUILD_ID;
|
|
50
|
+
return configured?.trim() || "unknown";
|
|
51
|
+
}
|
|
52
|
+
|
|
33
53
|
/**
|
|
34
54
|
* Auto-mount actions as HTTP endpoints under /_agent-native/actions/:name.
|
|
35
55
|
*
|
|
@@ -187,8 +207,8 @@ function handleOptionsRequest(event: any): string {
|
|
|
187
207
|
event,
|
|
188
208
|
"Access-Control-Allow-Headers",
|
|
189
209
|
cors.credentials
|
|
190
|
-
? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,${EMBED_TARGET_HEADER}`
|
|
191
|
-
: `${MCP_EMBED_CORS_ALLOW_HEADERS},X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend`,
|
|
210
|
+
? `Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-User-Timezone,X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,${EMBED_TARGET_HEADER}`
|
|
211
|
+
: `${MCP_EMBED_CORS_ALLOW_HEADERS},X-Agent-Native-Tool-Bridge,X-Agent-Native-Tool-Id,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id`,
|
|
192
212
|
);
|
|
193
213
|
}
|
|
194
214
|
|
|
@@ -320,6 +340,11 @@ export function mountActionRoutes(
|
|
|
320
340
|
}
|
|
321
341
|
|
|
322
342
|
setResponseHeader(event, "Cache-Control", "no-store");
|
|
343
|
+
setResponseHeader(
|
|
344
|
+
event,
|
|
345
|
+
"Access-Control-Expose-Headers",
|
|
346
|
+
"X-Agent-Native-Client-Mismatch,X-Agent-Native-Build-Id,X-Agent-Native-Client-Compatibility",
|
|
347
|
+
);
|
|
323
348
|
|
|
324
349
|
// Allow the declared method
|
|
325
350
|
if (effectiveMethod !== method) {
|
|
@@ -327,6 +352,31 @@ export function mountActionRoutes(
|
|
|
327
352
|
return { error: `Method not allowed. Use ${method}.` };
|
|
328
353
|
}
|
|
329
354
|
|
|
355
|
+
const requiredCompatibility = requiredClientCompatibilityVersion();
|
|
356
|
+
if (isFrontendActionRequest(event) && requiredCompatibility) {
|
|
357
|
+
const receivedCompatibility = getHeader(
|
|
358
|
+
event,
|
|
359
|
+
"x-agent-native-client-compatibility",
|
|
360
|
+
);
|
|
361
|
+
if (receivedCompatibility !== requiredCompatibility) {
|
|
362
|
+
const serverBuildId = currentBuildId();
|
|
363
|
+
setResponseStatus(event, 409);
|
|
364
|
+
setResponseHeader(event, "X-Agent-Native-Client-Mismatch", "1");
|
|
365
|
+
setResponseHeader(event, "X-Agent-Native-Build-Id", serverBuildId);
|
|
366
|
+
setResponseHeader(
|
|
367
|
+
event,
|
|
368
|
+
"X-Agent-Native-Client-Compatibility",
|
|
369
|
+
requiredCompatibility,
|
|
370
|
+
);
|
|
371
|
+
return {
|
|
372
|
+
error: "This browser tab must reload before it can use this app.",
|
|
373
|
+
code: "client_build_mismatch",
|
|
374
|
+
serverBuildId,
|
|
375
|
+
requiredCompatibility,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
330
380
|
// (audit H5) Per-action `toolCallable` opt-out for the tools-iframe
|
|
331
381
|
// bridge. The bridge tags every outbound action call with
|
|
332
382
|
// X-Agent-Native-Tool-Bridge: 1. When that header is present and the
|
|
@@ -4,16 +4,21 @@ import {
|
|
|
4
4
|
getH3App,
|
|
5
5
|
awaitBootstrap,
|
|
6
6
|
markDefaultPluginProvided,
|
|
7
|
+
trackPluginInit,
|
|
7
8
|
} from "./framework-request-handler.js";
|
|
8
9
|
|
|
9
10
|
type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
|
|
10
11
|
|
|
11
12
|
export function createAuthPlugin(options?: AuthOptions): NitroPluginDef {
|
|
12
|
-
return
|
|
13
|
+
return (nitroApp: any) => {
|
|
13
14
|
markDefaultPluginProvided(nitroApp, "auth");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const initPromise = (async () => {
|
|
16
|
+
await awaitBootstrap(nitroApp);
|
|
17
|
+
await autoMountAuth(getH3App(nitroApp), options);
|
|
18
|
+
})();
|
|
19
|
+
trackPluginInit(nitroApp, initPromise, {
|
|
20
|
+
paths: ["/_agent-native/auth"],
|
|
21
|
+
});
|
|
17
22
|
};
|
|
18
23
|
}
|
|
19
24
|
|
|
@@ -317,8 +317,12 @@ export function resolveSignupTrackingProperties(): Record<string, string> {
|
|
|
317
317
|
export function shouldSkipEmailVerification(): boolean {
|
|
318
318
|
const value = process.env.AUTH_SKIP_EMAIL_VERIFICATION;
|
|
319
319
|
if (value == null) {
|
|
320
|
+
const deployContext =
|
|
321
|
+
process.env.AGENT_NATIVE_BUILD_DEPLOY_CONTEXT || process.env.CONTEXT;
|
|
320
322
|
return (
|
|
321
|
-
process.env.NODE_ENV === "development" ||
|
|
323
|
+
process.env.NODE_ENV === "development" ||
|
|
324
|
+
process.env.NODE_ENV === "test" ||
|
|
325
|
+
deployContext === "deploy-preview"
|
|
322
326
|
);
|
|
323
327
|
}
|
|
324
328
|
const normalized = value.trim().toLowerCase();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const MCP_EMBED_CORS_ALLOW_HEADERS =
|
|
2
|
-
"Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-Agent-Native-Frontend,X-User-Timezone,X-Agent-Native-Embed-Target,X-Agent-Native-Embed-Transplant";
|
|
2
|
+
"Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF,X-Agent-Native-Frontend,X-Agent-Native-Client-Compatibility,X-Agent-Native-Build-Id,X-User-Timezone,X-Agent-Native-Embed-Target,X-Agent-Native-Embed-Transplant";
|
|
3
3
|
export const EMBED_TRANSPLANT_HEADER = "x-agent-native-embed-transplant";
|
|
4
4
|
|
|
5
5
|
const CLAUDE_MCP_CONTENT_HOST_RE = /^[a-f0-9]{32}\.claudemcpcontent\.com$/i;
|
|
@@ -1351,6 +1351,12 @@ export interface ClientConfigOptions {
|
|
|
1351
1351
|
optimizeDeps?: NonNullable<UserConfig["optimizeDeps"]>;
|
|
1352
1352
|
/** Additional Vite define constants. */
|
|
1353
1353
|
define?: UserConfig["define"];
|
|
1354
|
+
/**
|
|
1355
|
+
* Browser/server compatibility epoch for app changes that cannot safely run
|
|
1356
|
+
* across a cached client and a newer action backend. Bump only for an
|
|
1357
|
+
* incompatible protocol or data-model transition, not for every deploy.
|
|
1358
|
+
*/
|
|
1359
|
+
clientCompatibilityVersion?: string;
|
|
1354
1360
|
/**
|
|
1355
1361
|
* Framework route warmup behavior mounted by AgentSidebar.
|
|
1356
1362
|
*
|
|
@@ -2685,6 +2691,13 @@ function createAgentNativeConfig(
|
|
|
2685
2691
|
userConfig: UserConfig = {},
|
|
2686
2692
|
): UserConfig {
|
|
2687
2693
|
const cwd = process.cwd();
|
|
2694
|
+
const buildId =
|
|
2695
|
+
process.env.DEPLOY_ID?.trim() ||
|
|
2696
|
+
process.env.COMMIT_REF?.trim() ||
|
|
2697
|
+
process.env.VERCEL_GIT_COMMIT_SHA?.trim() ||
|
|
2698
|
+
process.env.CF_PAGES_COMMIT_SHA?.trim() ||
|
|
2699
|
+
process.env.AGENT_NATIVE_BUILD_SHA?.trim() ||
|
|
2700
|
+
"development";
|
|
2688
2701
|
|
|
2689
2702
|
// Workspace env fallback. If this app is inside a workspace, tell Vite to
|
|
2690
2703
|
// also look for .env files at the workspace root. Per-app .env still wins
|
|
@@ -2769,6 +2782,10 @@ function createAgentNativeConfig(
|
|
|
2769
2782
|
define: {
|
|
2770
2783
|
...(userConfig.define ?? {}),
|
|
2771
2784
|
...(options.define ?? {}),
|
|
2785
|
+
__AGENT_NATIVE_BUILD_ID__: JSON.stringify(buildId),
|
|
2786
|
+
__AGENT_NATIVE_CLIENT_COMPATIBILITY_VERSION__: JSON.stringify(
|
|
2787
|
+
options.clientCompatibilityVersion?.trim() || "",
|
|
2788
|
+
),
|
|
2772
2789
|
__AGENT_NATIVE_BUILD_GA_MEASUREMENT_ID__: JSON.stringify(
|
|
2773
2790
|
process.env.GA_MEASUREMENT_ID?.trim() || "",
|
|
2774
2791
|
),
|
|
@@ -134,6 +134,8 @@ cd templates/content && pnpm action <name> [args]
|
|
|
134
134
|
| `ensure-content-spaces` | | Idempotently provision the signed-in user's personal and current organization Content spaces, system databases, and catalog references |
|
|
135
135
|
| `backfill-content-files` | | Assign legacy pages to Content spaces and reconcile exactly one canonical Files database membership per non-system page |
|
|
136
136
|
| `list-content-spaces` | | List already-provisioned Content spaces authorized by the signed-in user's current ownership or organization memberships |
|
|
137
|
+
| `create-content-space` | `--name <name> --requestId <opaque-id>` | Idempotently create a private named Content workspace with its own canonical Files database |
|
|
138
|
+
| `delete-content-space` | `--spaceId <id>` | Permanently delete a user-created Content workspace and all of its content; Personal and organization workspaces are protected |
|
|
137
139
|
| `create-content-database` | `[--documentId <id>] [--spaceId <id>] [--parentId <id>] [--title <text>] [--description <text>]` | Create a described database page in a Content space or convert an existing page into a database |
|
|
138
140
|
| `create-inline-content-database` | `--hostDocumentId <id> [--title <text>] [--description <text>]` | Create a described database owned by an inline database block in the host document |
|
|
139
141
|
| `get-content-database` | `--databaseId <id>` or `--documentId <id>` | Get a database with its description, property/option schema guidance, item pages, and computed ancestry context |
|
|
@@ -4,8 +4,12 @@ import { and, eq, isNull } from "drizzle-orm";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { CONTENT_DATABASE_PERSONAL_VIEW_OVERRIDES_VERSION } from "../shared/api.js";
|
|
8
|
+
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
9
|
+
import { filesParentPropertyId } from "./_files-system-properties.js";
|
|
7
10
|
|
|
8
|
-
export const PERSONAL_DATABASE_VIEW_OVERRIDES_VERSION =
|
|
11
|
+
export const PERSONAL_DATABASE_VIEW_OVERRIDES_VERSION =
|
|
12
|
+
CONTENT_DATABASE_PERSONAL_VIEW_OVERRIDES_VERSION;
|
|
9
13
|
|
|
10
14
|
export const personalDatabaseViewSettingKey = (databaseId: string) =>
|
|
11
15
|
`content-database-personal-view:${databaseId}`;
|
|
@@ -38,8 +42,7 @@ export const filterSchema = z.object({
|
|
|
38
42
|
parentFilterGroupId: z.string().optional(),
|
|
39
43
|
});
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
version: z.literal(PERSONAL_DATABASE_VIEW_OVERRIDES_VERSION),
|
|
45
|
+
const personalViewOverridesFields = {
|
|
43
46
|
activeViewId: z.string().optional(),
|
|
44
47
|
views: z.array(
|
|
45
48
|
z.object({
|
|
@@ -49,6 +52,16 @@ export const personalViewOverridesSchema = z.object({
|
|
|
49
52
|
filterMode: z.enum(["and", "or"]).default("and"),
|
|
50
53
|
}),
|
|
51
54
|
),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const personalViewOverridesSchema = z.object({
|
|
58
|
+
version: z.literal(PERSONAL_DATABASE_VIEW_OVERRIDES_VERSION),
|
|
59
|
+
...personalViewOverridesFields,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const legacyPersonalViewOverridesSchema = z.object({
|
|
63
|
+
version: z.literal(1),
|
|
64
|
+
...personalViewOverridesFields,
|
|
52
65
|
});
|
|
53
66
|
|
|
54
67
|
export async function assertContentDatabaseViewerAccess(databaseId: string) {
|
|
@@ -64,7 +77,12 @@ export async function assertContentDatabaseViewerAccess(databaseId: string) {
|
|
|
64
77
|
);
|
|
65
78
|
if (!database) throw new Error(`Database "${databaseId}" not found`);
|
|
66
79
|
|
|
67
|
-
|
|
80
|
+
try {
|
|
81
|
+
await assertAccess("document", database.documentId, "viewer");
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (database.systemRole !== "files" || !database.spaceId) throw error;
|
|
84
|
+
await resolveContentSpaceAccess(database.spaceId);
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
export async function readPersonalDatabaseViewOverrides(
|
|
@@ -76,5 +94,31 @@ export async function readPersonalDatabaseViewOverrides(
|
|
|
76
94
|
personalDatabaseViewSettingKey(databaseId),
|
|
77
95
|
);
|
|
78
96
|
const parsed = personalViewOverridesSchema.safeParse(stored);
|
|
79
|
-
|
|
97
|
+
if (parsed.success) return parsed.data;
|
|
98
|
+
|
|
99
|
+
const legacy = legacyPersonalViewOverridesSchema.safeParse(stored);
|
|
100
|
+
if (!legacy.success) return null;
|
|
101
|
+
const [database] = await getDb()
|
|
102
|
+
.select({ systemRole: schema.contentDatabases.systemRole })
|
|
103
|
+
.from(schema.contentDatabases)
|
|
104
|
+
.where(eq(schema.contentDatabases.id, databaseId));
|
|
105
|
+
const legacyParentKey = filesParentPropertyId(databaseId);
|
|
106
|
+
return {
|
|
107
|
+
...legacy.data,
|
|
108
|
+
version: PERSONAL_DATABASE_VIEW_OVERRIDES_VERSION,
|
|
109
|
+
views: legacy.data.views.map((view) => ({
|
|
110
|
+
...view,
|
|
111
|
+
filters:
|
|
112
|
+
database?.systemRole === "files"
|
|
113
|
+
? view.filters.filter(
|
|
114
|
+
(filter) =>
|
|
115
|
+
!(
|
|
116
|
+
filter.key === legacyParentKey &&
|
|
117
|
+
filter.operator === "is_empty" &&
|
|
118
|
+
filter.value === ""
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
: view.filters,
|
|
122
|
+
})),
|
|
123
|
+
};
|
|
80
124
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
2
|
+
import { resolveAccess } from "@agent-native/core/sharing";
|
|
3
|
+
|
|
4
|
+
import { listContentOrganizationMemberships } from "./_content-space-access.js";
|
|
5
|
+
|
|
6
|
+
export async function resolveContentDocumentAccess(documentId: string) {
|
|
7
|
+
const current = await resolveAccess("document", documentId);
|
|
8
|
+
if (current) return current;
|
|
9
|
+
|
|
10
|
+
const userEmail = getRequestUserEmail();
|
|
11
|
+
if (!userEmail) return null;
|
|
12
|
+
const memberships = await listContentOrganizationMemberships(userEmail);
|
|
13
|
+
for (const membership of memberships) {
|
|
14
|
+
const access = await resolveAccess("document", documentId, {
|
|
15
|
+
userEmail,
|
|
16
|
+
orgId: membership.orgId,
|
|
17
|
+
});
|
|
18
|
+
if (access) return access;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|