@vexcms/next 0.1.0-alpha.16 → 0.1.0-alpha.19

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/README.md CHANGED
@@ -10,18 +10,30 @@ pnpm add @vexcms/next@alpha
10
10
 
11
11
  ## Quick Setup
12
12
 
13
+ ```tsx
14
+ // app/admin/clientProviders.tsx
15
+ "use client"
16
+ import { VexConfigProvider } from "@vexcms/react"
17
+ import config from "@/vex.config"
18
+
19
+ export function ClientProviders({ children }: { children: React.ReactNode }) {
20
+ return <VexConfigProvider config={config}>{children}</VexConfigProvider>
21
+ }
22
+ ```
23
+
13
24
  ```tsx
14
25
  // app/admin/layout.tsx
15
26
  import { NextAdminLayout } from "@vexcms/next/client"
16
- import config from "@/vex.config"
17
27
  import { getCurrentUser } from "@/auth/serverUtils"
18
28
 
29
+ import { ClientProviders } from "./clientProviders"
30
+
19
31
  export default async function AdminRootLayout({ children }: { children: React.ReactNode }) {
20
32
  const user = await getCurrentUser()
21
33
  return (
22
- <NextAdminLayout config={config} user={user ?? undefined}>
23
- {children}
24
- </NextAdminLayout>
34
+ <ClientProviders>
35
+ <NextAdminLayout user={user ?? undefined}>{children}</NextAdminLayout>
36
+ </ClientProviders>
25
37
  )
26
38
  }
27
39
  ```
@@ -29,7 +41,7 @@ export default async function AdminRootLayout({ children }: { children: React.Re
29
41
  ```tsx
30
42
  // app/admin/[[...path]]/page.tsx
31
43
  import { NextAdminPage } from "@vexcms/next/server"
32
- import config from "@/vex.config"
44
+ import config from "@/vex.config.server"
33
45
 
34
46
  export default function AdminPage({
35
47
  params,
@@ -62,7 +74,7 @@ Versioning, drafts, and live preview are not implemented yet — both are in pro
62
74
 
63
75
  ### Access Control
64
76
 
65
- - **Access provider** — `VexAccessProvider` supplies the RBAC access matrix to the admin panel via React context
77
+ - **Config provider** — `VexConfigProvider` (`@vexcms/react`) supplies the resolved client config — including `access` and `storage.clientUploads` — to the admin panel via React context, mounted once by your app around the admin route
66
78
  - **Collection-level permissions** — `usePermission` gates create, read, update, and delete per collection and per global
67
79
  - **Field-level permissions** — a per-field access rule hides a read-denied input entirely and renders an update-denied one read-only; edit forms submit only the fields the user actually changed
68
80
  - **UI enforcement** — Buttons and actions are disabled or hidden when the current user lacks permission
@@ -1,44 +1,35 @@
1
1
  import type { ReactNode } from "react";
2
- import type { VexConfig } from "@vexcms/core";
3
2
  import type { AdminUser } from "@vexcms/react";
4
3
  /**
5
4
  * Next.js admin layout shell for VexCMS.
6
5
  *
7
- * A **server component** that sits on the server→client boundary for the admin
8
- * panel. It sanitizes the resolved `vex.config` with `sanitizeConfigForClient`
9
- * — stripping storage adapter class instances, functions, and React components
10
- * — *before* the config is handed to the client leaf `NextAdminLayoutClient`.
11
- *
12
- * This placement is the whole point: React serializes props when a server
13
- * component renders a client component, so the sanitize **must** happen on the
14
- * server side of that edge. Sanitizing inside the client component would run
15
- * too late — the raw config would already have failed to serialize.
16
- *
17
- * The client leaf handles everything that needs the browser (`usePathname`,
18
- * `NuqsAdapter`, `NextLink`/`NextImage`); this wrapper does no client work.
6
+ * A **server component** that reads the `sidebar_state` cookie server-side
7
+ * (so the sidebar's open/closed state is correct on first paint, with no
8
+ * client flash) and passes it, plus `user`/`organization`/`children`, to the
9
+ * client leaf `NextAdminLayoutClient`. Config no longer crosses this
10
+ * boundary at all — the admin panel's client tree gets its config from the
11
+ * app's own `VexConfigProvider` mount (`clientProviders.tsx`), not from a
12
+ * prop threaded through this layout.
19
13
  *
20
14
  * Render it from your admin `layout.tsx`. Because this is a server component,
21
15
  * your `layout.tsx` may itself be an `async` server component (e.g. to run auth
22
- * checks) and pass the **raw** `vex.config` straight through — no sanitize
23
- * needed on the application side.
16
+ * checks).
24
17
  *
25
- * @param props - Layout props
26
- * @param props.config - The resolved VexCMS config from `vex.config.ts` (raw)
27
- * @param props.children - The page content from `[[...slug]]/page.tsx`
28
- * @param props.user - Current user for the admin shell
29
- * @returns The rendered admin shell, wrapping `children` in the sanitized-config
30
- * client boundary ({@link NextAdminLayoutClient}).
18
+ * @param props - Layout props.
19
+ * @param props.children - The page content from `[[...slug]]/page.tsx`.
20
+ * @param props.user - Current user for the admin shell.
21
+ * @returns The rendered admin shell, wrapping `children` in the client boundary
22
+ * ({@link NextAdminLayoutClient}) with the server-read sidebar cookie state.
31
23
  * @example
32
24
  * ```tsx
33
25
  * // app/admin/layout.tsx
34
26
  * import { NextAdminLayout } from "@vexcms/next/client";
35
- * import config from "~/vex.config";
36
27
  * import { getCurrentUser } from "~/auth/serverUtils";
37
28
  *
38
29
  * export default async function AdminLayout({ children }: { children: ReactNode }) {
39
30
  * const user = await getCurrentUser();
40
31
  * return (
41
- * <NextAdminLayout config={config} user={user ?? undefined}>
32
+ * <NextAdminLayout user={user ?? undefined}>
42
33
  * {children}
43
34
  * </NextAdminLayout>
44
35
  * );
@@ -46,7 +37,6 @@ import type { AdminUser } from "@vexcms/react";
46
37
  * ```
47
38
  */
48
39
  export declare function NextAdminLayout(props: {
49
- config: VexConfig;
50
40
  children: ReactNode;
51
41
  user?: AdminUser;
52
42
  organization?: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"NextAdminLayout.d.ts","sourceRoot":"","sources":["../src/NextAdminLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,wCAmBA"}
1
+ {"version":3,"file":"NextAdminLayout.d.ts","sourceRoot":"","sources":["../src/NextAdminLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC,wCAaA"}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NextAdminLayout
3
- } from "./chunk-YCJNIS6O.js";
3
+ } from "./chunk-K4AAS5B5.js";
4
4
  export {
5
5
  NextAdminLayout
6
6
  };
@@ -1,5 +1,4 @@
1
1
  import type { ReactNode } from "react";
2
- import type { ClientVexConfig } from "@vexcms/core";
3
2
  import { type AdminUser } from "@vexcms/react";
4
3
  /**
5
4
  * Client leaf for the Next.js admin layout.
@@ -12,24 +11,21 @@ import { type AdminUser } from "@vexcms/react";
12
11
  * - `NextLink` / `NextImage` passed as framework components so every
13
12
  * `VexLink`/`VexImage` uses Next.js routing and image optimisation.
14
13
  *
15
- * **Receives an already-sanitized `ClientVexConfig`.** The server wrapper
16
- * ({@link NextAdminLayout}) calls `sanitizeConfigForClient` before this
17
- * component is reached, so no non-serializable values (storage adapter class
18
- * instances, functions, React components) cross the server→client boundary.
14
+ * Config reaches `AdminLayout`'s children through the app's own
15
+ * `VexConfigProvider` mount, not through a prop on this component — this
16
+ * leaf never touches config at all.
19
17
  *
20
18
  * This component is internal to `@vexcms/next` — applications render
21
19
  * {@link NextAdminLayout} instead.
22
20
  *
23
- * @param props - Layout props
24
- * @param props.config - Sanitized config, safe to serialize across the boundary
25
- * @param props.children - The page content from `[[...slug]]/page.tsx`
26
- * @param props.user - Current user for the admin shell
21
+ * @param props - Layout props.
22
+ * @param props.children - The page content from `[[...slug]]/page.tsx`.
23
+ * @param props.user - Current user for the admin shell.
27
24
  * @returns The admin shell rendered from `children`, wrapped in `NuqsAdapter`
28
25
  * and the shared `AdminLayout`, wired with the active slug/document id derived
29
26
  * from `usePathname()` and Next.js `Link`/`Image` components.
30
27
  */
31
28
  export declare function NextAdminLayoutClient(props: {
32
- config: ClientVexConfig;
33
29
  children: ReactNode;
34
30
  user?: AdminUser;
35
31
  organization?: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"NextAdminLayoutClient.d.ts","sourceRoot":"","sources":["../src/NextAdminLayoutClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAKpD,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAwBA"}
1
+ {"version":3,"file":"NextAdminLayoutClient.d.ts","sourceRoot":"","sources":["../src/NextAdminLayoutClient.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAuBA"}
@@ -16,7 +16,6 @@ function NextAdminLayoutClient(props) {
16
16
  return /* @__PURE__ */ jsx(NuqsAdapter, { children: /* @__PURE__ */ jsx(
17
17
  AdminLayout,
18
18
  {
19
- config: props.config,
20
19
  activeSlug,
21
20
  components: { Link: NextLink, Image: NextImage },
22
21
  pathname,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/NextAdminLayoutClient.tsx"],"sourcesContent":["\"use client\";\n// \"use client\" is injected by the tsup build banner for this entry (see\n// tsup.config.ts) so the emitted module carries the client boundary.\nimport type { ReactNode } from \"react\";\nimport type { ClientVexConfig } from \"@vexcms/core\";\nimport { usePathname } from \"next/navigation\";\nimport NextLink from \"next/link\";\nimport NextImage from \"next/image\";\nimport { NuqsAdapter } from \"nuqs/adapters/next/app\";\nimport { AdminLayout, type AdminUser } from \"@vexcms/react\";\n\n/**\n * Client leaf for the Next.js admin layout.\n *\n * Owns all client-only wiring that cannot run on the server:\n * - `usePathname()` to derive the active collection slug / document id for\n * sidebar highlighting (Next.js layouts do not receive route params).\n * - `NuqsAdapter` (`nuqs/adapters/next/app`) so URL state works inside the\n * admin routes.\n * - `NextLink` / `NextImage` passed as framework components so every\n * `VexLink`/`VexImage` uses Next.js routing and image optimisation.\n *\n * **Receives an already-sanitized `ClientVexConfig`.** The server wrapper\n * ({@link NextAdminLayout}) calls `sanitizeConfigForClient` before this\n * component is reached, so no non-serializable values (storage adapter class\n * instances, functions, React components) cross the server→client boundary.\n *\n * This component is internal to `@vexcms/next` — applications render\n * {@link NextAdminLayout} instead.\n *\n * @param props - Layout props\n * @param props.config - Sanitized config, safe to serialize across the boundary\n * @param props.children - The page content from `[[...slug]]/page.tsx`\n * @param props.user - Current user for the admin shell\n * @returns The admin shell rendered from `children`, wrapped in `NuqsAdapter`\n * and the shared `AdminLayout`, wired with the active slug/document id derived\n * from `usePathname()` and Next.js `Link`/`Image` components.\n */\nexport function NextAdminLayoutClient(props: {\n config: ClientVexConfig;\n children: ReactNode;\n user?: AdminUser;\n organization?: Record<string, unknown>;\n sidebarOpen?: boolean;\n}) {\n const pathname = usePathname();\n // pathname: \"/admin\", \"/admin/posts\", \"/admin/posts/123\"\n // Split on \"/\" and take the segment after \"admin\"\n const segments = pathname.split(\"/\").filter(Boolean);\n const activeSlug = segments[1]; // undefined on /admin, \"posts\" on /admin/posts\n const activeDocID = segments[2];\n\n return (\n <NuqsAdapter>\n <AdminLayout\n config={props.config}\n activeSlug={activeSlug}\n components={{ Link: NextLink, Image: NextImage }}\n pathname={pathname}\n activeDocID={activeDocID}\n user={props.user}\n organization={props.organization}\n sidebarOpen={props.sidebarOpen}\n >\n {props.children}\n </AdminLayout>\n </NuqsAdapter>\n );\n}\n"],"mappings":";;;;AAKA,SAAS,mBAAmB;AAC5B,OAAO,cAAc;AACrB,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAC5B,SAAS,mBAAmC;AA6CtC;AAhBC,SAAS,sBAAsB,OAMnC;AACD,QAAM,WAAW,YAAY;AAG7B,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACnD,QAAM,aAAa,SAAS,CAAC;AAC7B,QAAM,cAAc,SAAS,CAAC;AAE9B,SACE,oBAAC,eACC;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ,MAAM;AAAA,MACd;AAAA,MACA,YAAY,EAAE,MAAM,UAAU,OAAO,UAAU;AAAA,MAC/C;AAAA,MACA;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,cAAc,MAAM;AAAA,MACpB,aAAa,MAAM;AAAA,MAElB,gBAAM;AAAA;AAAA,EACT,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/NextAdminLayoutClient.tsx"],"sourcesContent":["\"use client\";\n// \"use client\" is injected by the tsup build banner for this entry (see\n// tsup.config.ts) so the emitted module carries the client boundary.\nimport type { ReactNode } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport NextLink from \"next/link\";\nimport NextImage from \"next/image\";\nimport { NuqsAdapter } from \"nuqs/adapters/next/app\";\nimport { AdminLayout, type AdminUser } from \"@vexcms/react\";\n\n/**\n * Client leaf for the Next.js admin layout.\n *\n * Owns all client-only wiring that cannot run on the server:\n * - `usePathname()` to derive the active collection slug / document id for\n * sidebar highlighting (Next.js layouts do not receive route params).\n * - `NuqsAdapter` (`nuqs/adapters/next/app`) so URL state works inside the\n * admin routes.\n * - `NextLink` / `NextImage` passed as framework components so every\n * `VexLink`/`VexImage` uses Next.js routing and image optimisation.\n *\n * Config reaches `AdminLayout`'s children through the app's own\n * `VexConfigProvider` mount, not through a prop on this component — this\n * leaf never touches config at all.\n *\n * This component is internal to `@vexcms/next` — applications render\n * {@link NextAdminLayout} instead.\n *\n * @param props - Layout props.\n * @param props.children - The page content from `[[...slug]]/page.tsx`.\n * @param props.user - Current user for the admin shell.\n * @returns The admin shell rendered from `children`, wrapped in `NuqsAdapter`\n * and the shared `AdminLayout`, wired with the active slug/document id derived\n * from `usePathname()` and Next.js `Link`/`Image` components.\n */\nexport function NextAdminLayoutClient(props: {\n children: ReactNode;\n user?: AdminUser;\n organization?: Record<string, unknown>;\n sidebarOpen?: boolean;\n}) {\n const pathname = usePathname();\n // pathname: \"/admin\", \"/admin/posts\", \"/admin/posts/123\"\n // Split on \"/\" and take the segment after \"admin\"\n const segments = pathname.split(\"/\").filter(Boolean);\n const activeSlug = segments[1]; // undefined on /admin, \"posts\" on /admin/posts\n const activeDocID = segments[2];\n\n return (\n <NuqsAdapter>\n <AdminLayout\n activeSlug={activeSlug}\n components={{ Link: NextLink, Image: NextImage }}\n pathname={pathname}\n activeDocID={activeDocID}\n user={props.user}\n organization={props.organization}\n sidebarOpen={props.sidebarOpen}\n >\n {props.children}\n </AdminLayout>\n </NuqsAdapter>\n );\n}\n"],"mappings":";;;;AAIA,SAAS,mBAAmB;AAC5B,OAAO,cAAc;AACrB,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAC5B,SAAS,mBAAmC;AA0CtC;AAfC,SAAS,sBAAsB,OAKnC;AACD,QAAM,WAAW,YAAY;AAG7B,QAAM,WAAW,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACnD,QAAM,aAAa,SAAS,CAAC;AAC7B,QAAM,cAAc,SAAS,CAAC;AAE9B,SACE,oBAAC,eACC;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,YAAY,EAAE,MAAM,UAAU,OAAO,UAAU;AAAA,MAC/C;AAAA,MACA;AAAA,MACA,MAAM,MAAM;AAAA,MACZ,cAAc,MAAM;AAAA,MACpB,aAAa,MAAM;AAAA,MAElB,gBAAM;AAAA;AAAA,EACT,GACF;AAEJ;","names":[]}
@@ -4,7 +4,7 @@ import type { VexConfig } from "@vexcms/core";
4
4
  *
5
5
  * An `async` server component that routes by the `[[...slug]]` catch-all
6
6
  * params, prefetches Convex data via `fetchQuery`, and renders the correct
7
- * view component. Does **not** include a layout wrapper — `VexAdminLayout`
7
+ * view component. Does **not** include a layout wrapper — `NextAdminLayout`
8
8
  * in `app/admin/layout.tsx` owns the persistent shell.
9
9
  *
10
10
  * **Route mapping:**
@@ -16,22 +16,24 @@ import type { VexConfig } from "@vexcms/core";
16
16
  * | `[collectionSlug, documentId]` | `CollectionEditView` with preloaded doc |
17
17
  *
18
18
  * @param props - Component props
19
- * @param props.config - The resolved VexCMS configuration from `vex.config.ts`
19
+ * @param props.config - The resolved server `VexConfig`, typically the default export of
20
+ * `vex.config.server.ts` — not the client-safe `vex.config.ts`.
20
21
  * @param props.params - Next.js 15 async params `{ path?: string[] }`
22
+ * @param props.token - Optional session token forwarded to `fetchQuery` for authenticated reads.
21
23
  * @returns The appropriate admin view for the current URL path.
22
24
  *
23
25
  * @example
24
26
  * ```tsx
25
27
  * // app/admin/[[...slug]]/page.tsx
26
- * import { VexAdminPage } from "@vexcms/next";
27
- * import config from "../../../../vex.config";
28
+ * import { NextAdminPage } from "@vexcms/next/server";
29
+ * import config from "../../../../vex.config.server";
28
30
  *
29
31
  * export default function AdminPage({
30
32
  * params,
31
33
  * }: {
32
34
  * params: Promise<{ path?: string[] }>;
33
35
  * }) {
34
- * return <VexAdminPage config={config} params={params} />;
36
+ * return <NextAdminPage config={config} params={params} />;
35
37
  * }
36
38
  * ```
37
39
  */
@@ -1 +1 @@
1
- {"version":3,"file":"NextAdminPage.d.ts","sourceRoot":"","sources":["../src/NextAdminPage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoC,SAAS,EAAoB,MAAM,cAAc,CAAC;AAYlG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,wCAoIA"}
1
+ {"version":3,"file":"NextAdminPage.d.ts","sourceRoot":"","sources":["../src/NextAdminPage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoC,SAAS,EAAoB,MAAM,cAAc,CAAC;AAWlG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,wCAgHA"}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NextAdminPage
3
- } from "./chunk-TR3Z3P6Q.js";
3
+ } from "./chunk-LHJAXWLB.js";
4
4
  export {
5
5
  NextAdminPage
6
6
  };
@@ -1,16 +1,13 @@
1
1
  // src/NextAdminLayout.tsx
2
- import { sanitizeConfigForClient } from "@vexcms/core";
3
2
  import { NextAdminLayoutClient } from "./NextAdminLayoutClient";
4
3
  import { cookies } from "next/headers";
5
4
  import { jsx } from "react/jsx-runtime";
6
5
  async function NextAdminLayout(props) {
7
- const clientConfig = sanitizeConfigForClient(props.config);
8
6
  const cookieStore = await cookies();
9
7
  const sidebarOpen = String(cookieStore.get("sidebar_state")?.value) === "true";
10
8
  return /* @__PURE__ */ jsx(
11
9
  NextAdminLayoutClient,
12
10
  {
13
- config: clientConfig,
14
11
  user: props.user,
15
12
  organization: props.organization,
16
13
  sidebarOpen,
@@ -22,4 +19,4 @@ async function NextAdminLayout(props) {
22
19
  export {
23
20
  NextAdminLayout
24
21
  };
25
- //# sourceMappingURL=chunk-YCJNIS6O.js.map
22
+ //# sourceMappingURL=chunk-K4AAS5B5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/NextAdminLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { AdminUser } from \"@vexcms/react\";\nimport { NextAdminLayoutClient } from \"./NextAdminLayoutClient\";\nimport { cookies } from \"next/headers\";\n\n/**\n * Next.js admin layout shell for VexCMS.\n *\n * A **server component** that reads the `sidebar_state` cookie server-side\n * (so the sidebar's open/closed state is correct on first paint, with no\n * client flash) and passes it, plus `user`/`organization`/`children`, to the\n * client leaf `NextAdminLayoutClient`. Config no longer crosses this\n * boundary at all — the admin panel's client tree gets its config from the\n * app's own `VexConfigProvider` mount (`clientProviders.tsx`), not from a\n * prop threaded through this layout.\n *\n * Render it from your admin `layout.tsx`. Because this is a server component,\n * your `layout.tsx` may itself be an `async` server component (e.g. to run auth\n * checks).\n *\n * @param props - Layout props.\n * @param props.children - The page content from `[[...slug]]/page.tsx`.\n * @param props.user - Current user for the admin shell.\n * @returns The rendered admin shell, wrapping `children` in the client boundary\n * ({@link NextAdminLayoutClient}) with the server-read sidebar cookie state.\n * @example\n * ```tsx\n * // app/admin/layout.tsx\n * import { NextAdminLayout } from \"@vexcms/next/client\";\n * import { getCurrentUser } from \"~/auth/serverUtils\";\n *\n * export default async function AdminLayout({ children }: { children: ReactNode }) {\n * const user = await getCurrentUser();\n * return (\n * <NextAdminLayout user={user ?? undefined}>\n * {children}\n * </NextAdminLayout>\n * );\n * }\n * ```\n */\nexport async function NextAdminLayout(props: {\n children: ReactNode;\n user?: AdminUser;\n organization?: Record<string, unknown>;\n}) {\n const cookieStore = await cookies();\n const sidebarOpen = String(cookieStore.get(\"sidebar_state\")?.value) === \"true\";\n\n return (\n <NextAdminLayoutClient\n user={props.user}\n organization={props.organization}\n sidebarOpen={sidebarOpen}\n >\n {props.children}\n </NextAdminLayoutClient>\n );\n}\n"],"mappings":";AAEA,SAAS,6BAA6B;AACtC,SAAS,eAAe;AA+CpB;AATJ,eAAsB,gBAAgB,OAInC;AACD,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,cAAc,OAAO,YAAY,IAAI,eAAe,GAAG,KAAK,MAAM;AAExE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,MAAM;AAAA,MACZ,cAAc,MAAM;AAAA,MACpB;AAAA,MAEC,gBAAM;AAAA;AAAA,EACT;AAEJ;","names":[]}
@@ -10,20 +10,18 @@ import {
10
10
  GlobalsListView,
11
11
  GlobalEditView
12
12
  } from "@vexcms/react";
13
- import { sanitizeConfigForClient } from "@vexcms/core";
14
13
  import { jsx, jsxs } from "react/jsx-runtime";
15
14
  async function NextAdminPage(props) {
16
15
  const { path = [] } = await props.params;
17
16
  const [collectionSlug, documentId] = path;
18
- const clientConfig = sanitizeConfigForClient(props.config);
19
17
  if (!collectionSlug) {
20
- return /* @__PURE__ */ jsx(DashboardView, { config: clientConfig });
18
+ return /* @__PURE__ */ jsx(DashboardView, {});
21
19
  }
22
20
  if (collectionSlug === "globals") {
23
21
  if (!documentId) {
24
- return /* @__PURE__ */ jsx(GlobalsListView, { config: clientConfig });
22
+ return /* @__PURE__ */ jsx(GlobalsListView, {});
25
23
  }
26
- const globalConfig = clientConfig.globals.find((g) => g.slug === documentId);
24
+ const globalConfig = props.config.globals.find((g) => g.slug === documentId);
27
25
  if (!globalConfig) {
28
26
  return /* @__PURE__ */ jsxs("div", { children: [
29
27
  /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground p-6", children: [
@@ -39,10 +37,10 @@ async function NextAdminPage(props) {
39
37
  { slug: globalConfig.slug },
40
38
  props.token ? { token: props.token } : void 0
41
39
  );
42
- return /* @__PURE__ */ jsx(GlobalEditView, { global: globalConfig, initialData: global });
40
+ return /* @__PURE__ */ jsx(GlobalEditView, { global: globalConfig.slug, initialData: global });
43
41
  }
44
- const collection = clientConfig.collections.find((c) => c.slug === collectionSlug);
45
- const mediaCollection = clientConfig.mediaCollections.find((mc) => mc.slug === collectionSlug);
42
+ const collection = props.config.collections.find((c) => c.slug === collectionSlug);
43
+ const mediaCollection = props.config.mediaCollections.find((mc) => mc.slug === collectionSlug);
46
44
  if (!collection && !mediaCollection) {
47
45
  return /* @__PURE__ */ jsxs("div", { children: [
48
46
  /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground p-6", children: [
@@ -56,16 +54,13 @@ async function NextAdminPage(props) {
56
54
  if (mediaCollection && documentId) {
57
55
  const initialData2 = await fetchQuery(
58
56
  vexConvexApi.get,
59
- {
60
- id: documentId,
61
- collection: mediaCollection.slug
62
- },
57
+ { id: documentId, collection: mediaCollection.slug },
63
58
  props.token ? { token: props.token } : void 0
64
59
  );
65
60
  return /* @__PURE__ */ jsx(
66
61
  MediaCollectionEditView,
67
62
  {
68
- collection: mediaCollection,
63
+ collection: mediaCollection.slug,
69
64
  documentId,
70
65
  initialData: initialData2
71
66
  }
@@ -80,18 +75,14 @@ async function NextAdminPage(props) {
80
75
  vexConvexApi.find,
81
76
  {
82
77
  collection: collectionSlug,
83
- paginationOpts: {
84
- numItems: numItems2,
85
- totalDocs: true,
86
- cursor: null
87
- }
78
+ paginationOpts: { numItems: numItems2, totalDocs: true, cursor: null }
88
79
  },
89
80
  props.token ? { token: props.token } : void 0
90
81
  );
91
82
  return /* @__PURE__ */ jsx(
92
83
  MediaCollectionListView,
93
84
  {
94
- collection: mediaCollection,
85
+ collection: mediaCollection.slug,
95
86
  initialData: initialData2
96
87
  }
97
88
  );
@@ -99,16 +90,13 @@ async function NextAdminPage(props) {
99
90
  if (collection && documentId) {
100
91
  const initialData2 = await fetchQuery(
101
92
  vexConvexApi.get,
102
- {
103
- id: documentId,
104
- collection: collection.slug
105
- },
93
+ { id: documentId, collection: collection.slug },
106
94
  props.token ? { token: props.token } : void 0
107
95
  );
108
96
  return /* @__PURE__ */ jsx(
109
97
  CollectionEditView,
110
98
  {
111
- collection,
99
+ collection: collection.slug,
112
100
  documentId,
113
101
  initialData: initialData2
114
102
  }
@@ -126,18 +114,14 @@ async function NextAdminPage(props) {
126
114
  {
127
115
  collection: collectionSlug,
128
116
  depth: 1,
129
- paginationOpts: {
130
- cursor: null,
131
- numItems,
132
- totalDocs: true
133
- }
117
+ paginationOpts: { cursor: null, numItems, totalDocs: true }
134
118
  },
135
119
  props.token ? { token: props.token } : void 0
136
120
  );
137
- return /* @__PURE__ */ jsx(CollectionListView, { collection, initialData });
121
+ return /* @__PURE__ */ jsx(CollectionListView, { collection: collection.slug, initialData });
138
122
  }
139
123
 
140
124
  export {
141
125
  NextAdminPage
142
126
  };
143
- //# sourceMappingURL=chunk-TR3Z3P6Q.js.map
127
+ //# sourceMappingURL=chunk-LHJAXWLB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/NextAdminPage.tsx"],"sourcesContent":["import { fetchQuery } from \"convex/nextjs\";\nimport { vexConvexApi } from \"@vexcms/core\";\nimport type { CollectionSlug, PaginationResult, VexConfig, VexMediaDocument } from \"@vexcms/core\";\nimport {\n DashboardView,\n CollectionListView,\n CollectionEditView,\n MediaCollectionEditView,\n MediaCollectionListView,\n GlobalsListView,\n GlobalEditView,\n} from \"@vexcms/react\";\n\n/**\n * VexCMS admin page server component for Next.js.\n *\n * An `async` server component that routes by the `[[...slug]]` catch-all\n * params, prefetches Convex data via `fetchQuery`, and renders the correct\n * view component. Does **not** include a layout wrapper — `NextAdminLayout`\n * in `app/admin/layout.tsx` owns the persistent shell.\n *\n * **Route mapping:**\n * | `path` array | View |\n * |---|---|\n * | `[]` or undefined | `DashboardView` |\n * | `[collectionSlug]` | `CollectionListView` with preloaded docs |\n * | `[collectionSlug, \"new\"]` | `CollectionEditView` (empty form) |\n * | `[collectionSlug, documentId]` | `CollectionEditView` with preloaded doc |\n *\n * @param props - Component props\n * @param props.config - The resolved server `VexConfig`, typically the default export of\n * `vex.config.server.ts` — not the client-safe `vex.config.ts`.\n * @param props.params - Next.js 15 async params `{ path?: string[] }`\n * @param props.token - Optional session token forwarded to `fetchQuery` for authenticated reads.\n * @returns The appropriate admin view for the current URL path.\n *\n * @example\n * ```tsx\n * // app/admin/[[...slug]]/page.tsx\n * import { NextAdminPage } from \"@vexcms/next/server\";\n * import config from \"../../../../vex.config.server\";\n *\n * export default function AdminPage({\n * params,\n * }: {\n * params: Promise<{ path?: string[] }>;\n * }) {\n * return <NextAdminPage config={config} params={params} />;\n * }\n * ```\n */\nexport async function NextAdminPage(props: {\n config: VexConfig;\n params: Promise<{ path?: string[] }>;\n token?: string;\n}) {\n const { path = [] } = await props.params;\n const [collectionSlug, documentId] = path;\n\n if (!collectionSlug) {\n return <DashboardView />;\n }\n\n if (collectionSlug === \"globals\") {\n if (!documentId) {\n return <GlobalsListView />;\n }\n const globalConfig = props.config.globals.find((g) => g.slug === documentId);\n if (!globalConfig) {\n return (\n <div>\n <p className=\"text-muted-foreground p-6\">Global &quot;{documentId}&quot; not found.</p>\n <p>TODO: add not found view</p>\n </div>\n );\n }\n const global = await fetchQuery(\n vexConvexApi.globals.get,\n { slug: globalConfig.slug },\n props.token ? { token: props.token } : undefined,\n );\n return <GlobalEditView global={globalConfig.slug} initialData={global} />;\n }\n\n const collection = props.config.collections.find((c) => c.slug === collectionSlug);\n const mediaCollection = props.config.mediaCollections.find((mc) => mc.slug === collectionSlug);\n\n if (!collection && !mediaCollection) {\n return (\n <div>\n <p className=\"text-muted-foreground p-6\">\n Collection &quot;{collectionSlug}&quot; not found.\n </p>\n <p>TODO: add not found view</p>\n </div>\n );\n }\n\n if (mediaCollection && documentId) {\n const initialData = await fetchQuery(\n vexConvexApi.get,\n { id: documentId, collection: mediaCollection.slug },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <MediaCollectionEditView\n collection={mediaCollection.slug}\n documentId={documentId}\n initialData={initialData as VexMediaDocument | null}\n />\n );\n }\n\n if (mediaCollection) {\n const numItems = Math.max(\n mediaCollection.admin.table.serverPageSize,\n mediaCollection.admin.table.defaultPageSize,\n );\n const initialData = await fetchQuery(\n vexConvexApi.find,\n {\n collection: collectionSlug as CollectionSlug,\n paginationOpts: { numItems, totalDocs: true, cursor: null },\n },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <MediaCollectionListView\n collection={mediaCollection.slug}\n initialData={initialData as PaginationResult<VexMediaDocument>}\n />\n );\n }\n\n if (collection && documentId) {\n const initialData = await fetchQuery(\n vexConvexApi.get,\n { id: documentId, collection: collection.slug },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <CollectionEditView\n collection={collection.slug}\n documentId={documentId}\n initialData={initialData}\n />\n );\n }\n\n if (!collection) {\n throw new Error(\"invalid collection slug\");\n }\n\n const numItems = Math.max(\n collection.admin.table.serverPageSize,\n collection.admin.table.defaultPageSize,\n );\n const initialData = await fetchQuery(\n vexConvexApi.findPaginated,\n {\n collection: collectionSlug as CollectionSlug,\n depth: 1,\n paginationOpts: { cursor: null, numItems, totalDocs: true },\n },\n props.token ? { token: props.token } : undefined,\n );\n return <CollectionListView collection={collection.slug} initialData={initialData} />;\n}\n"],"mappings":"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","names":["initialData","numItems"]}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from "./NextAdminPage";
2
2
  export * from "./NextAdminLayout";
3
3
  export { relationship, defineCollection } from "@vexcms/react";
4
4
  export { text, number, select, date, url, checkbox, defineConfig, } from "@vexcms/react";
5
- export type { AdminUser, RelationshipFieldInput, RelationshipField, AdminCollectionConfigInput, CollectionConfigInput, CollectionConfig, TextField, NumberField, SelectField, DateField, UrlField, CheckboxField, AdminField, CollectionSlug, VexDocument, VexConfig, VexConfigInput, RelationshipPreviewProps, } from "@vexcms/react";
5
+ export type { AdminUser, RelationshipFieldInput, RelationshipField, AdminCollectionConfigInput, CollectionConfigInput, CollectionConfig, TextField, NumberField, SelectField, DateField, UrlField, CheckboxField, AdminField, CollectionSlug, VexDocument, VexConfig, VexClientConfig, VexClientConfigInput, VexServerConfigInput, RelationshipPreviewProps, } from "@vexcms/react";
6
6
  export type { RelationshipKeysOf, TextKeysOf, SortableKeysOf, RelationshipTargetOf, Populated, PopulateShape, } from "@vexcms/core";
7
7
  export { createVexServerClient } from "./cache/createVexServerClient";
8
8
  export type { VexServerClientOptions, VexServerClient } from "./cache/types";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAG/D,OAAO,EACL,IAAI,EACJ,MAAM,EACN,MAAM,EACN,IAAI,EACJ,GAAG,EACH,QAAQ,EACR,YAAY,GACb,MAAM,eAAe,CAAC;AAMvB,YAAY,EACV,SAAS,EAET,sBAAsB,EACtB,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,EACrB,gBAAgB,EAEhB,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,EACT,QAAQ,EACR,aAAa,EAEb,UAAU,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,cAAc,EACd,wBAAwB,GACzB,MAAM,eAAe,CAAC;AAYvB,YAAY,EACV,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,SAAS,EACT,aAAa,GACd,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AACtF,YAAY,EACX,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAG/D,OAAO,EACL,IAAI,EACJ,MAAM,EACN,MAAM,EACN,IAAI,EACJ,GAAG,EACH,QAAQ,EACR,YAAY,GACb,MAAM,eAAe,CAAC;AAMvB,YAAY,EACV,SAAS,EAET,sBAAsB,EACtB,iBAAiB,EACjB,0BAA0B,EAC1B,qBAAqB,EACrB,gBAAgB,EAEhB,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,EACT,QAAQ,EACR,aAAa,EAEb,UAAU,EACV,cAAc,EACd,WAAW,EACX,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AAYvB,YAAY,EACV,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,SAAS,EACT,aAAa,GACd,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,YAAY,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AACtF,YAAY,EACX,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  NextAdminPage
3
- } from "./chunk-TR3Z3P6Q.js";
3
+ } from "./chunk-LHJAXWLB.js";
4
4
  import {
5
5
  NextAdminLayout
6
- } from "./chunk-YCJNIS6O.js";
6
+ } from "./chunk-K4AAS5B5.js";
7
7
  import {
8
8
  createVexRevalidateRoute,
9
9
  createVexServerClient
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @vexcms/next v0.1.0-alpha.1\n//\n// Two distinct export surfaces:\n//\n// 1. Server / client component entries (sub-path imports — required because\n// \"use client\" can't be mixed with async server components in one bundle):\n// import { NextAdminPage } from \"@vexcms/next/server\" ← async server component\n// import { NextAdminLayout } from \"@vexcms/next/client\" ← \"use client\" component\n//\n// 2. Config / type re-exports (everything below). Re-exports `@vexcms/react`'s\n// HKT-bound config functions and types transitively so Next consumers can\n// import every config function from a single package:\n//\n// import {\n// defineConfig,\n// defineCollection,\n// relationship,\n// text, number, select, date, url, checkbox,\n// } from \"@vexcms/next\";\n//\n// `admin.components.preview` slots remain typed as React `ComponentType<P>`\n// via `ReactHKT` because @vexcms/react bound it before re-exporting. See\n// spec 22 Decision 15 for the architectural rationale.\n\n// Server / client surface\nexport * from \"./NextAdminPage\";\nexport * from \"./NextAdminLayout\";\n\n// Config functions — HKT-bound wrappers from @vexcms/react\nexport { relationship, defineCollection } from \"@vexcms/react\";\n\n// Field config functions without component slots (pure pass-through)\nexport {\n text,\n number,\n select,\n date,\n url,\n checkbox,\n defineConfig,\n} from \"@vexcms/react\";\n\n// Public types — HKT-bound where applicable.\n// `*FieldInput` core config types intentionally not re-exported (they collide\n// with React `*FieldInput` component names; users get them via inference\n// through the config functions).\nexport type {\n AdminUser,\n // HKT-bound (admin.components.preview = React ComponentType)\n RelationshipFieldInput,\n RelationshipField,\n AdminCollectionConfigInput,\n CollectionConfigInput,\n CollectionConfig,\n // Resolved field types\n TextField,\n NumberField,\n SelectField,\n DateField,\n UrlField,\n CheckboxField,\n // Shared types\n AdminField,\n CollectionSlug,\n VexDocument,\n VexConfig,\n VexConfigInput,\n RelationshipPreviewProps,\n} from \"@vexcms/react\";\n\n// API functions are NOT re-exported from this barrel because @vexcms/next's\n// root index.ts is imported by server components (NextAdminPage). Pulling\n// @vexcms/core/client here would transitively import @convex-dev/react-query\n// which uses createContext — crashing RSC.\n//\n// Import directly from the correct environment-specific path instead:\n//\n// import { find, get, search } from \"@vexcms/core/client\"; ← React components\n// import { find, get, search } from \"@vexcms/core/server\"; ← Convex handlers\n\nexport type {\n RelationshipKeysOf,\n TextKeysOf,\n SortableKeysOf,\n RelationshipTargetOf,\n Populated,\n PopulateShape,\n} from \"@vexcms/core\";\n\n// Cache / SEO surface — server-only, no client context, so these are safe in\n// this RSC-imported root barrel. Also reachable via the \"@vexcms/next/cache\"\n// and \"@vexcms/next/seo\" subpaths.\nexport { createVexServerClient } from \"./cache/createVexServerClient\";\nexport type { VexServerClientOptions, VexServerClient } from \"./cache/types\";\nexport { createVexRobots } from \"./seo/createVexRobots\";\nexport { createVexSitemap } from \"./seo/createVexSitemap\";\nexport { vexMetadata } from \"./seo/vexMetadata\";\nexport { vexStaticParams } from \"./seo/vexStaticParams\";\nexport { createVexRevalidateRoute } from \"./cache/createVexRevalidateRoute\";\nexport type { CreateVexRevalidateRouteProps } from \"./cache/createVexRevalidateRoute\";\nexport type {\n} from \"./cache/types\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,SAAS,cAAc,wBAAwB;AAG/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// @vexcms/next v0.1.0-alpha.1\n//\n// Two distinct export surfaces:\n//\n// 1. Server / client component entries (sub-path imports — required because\n// \"use client\" can't be mixed with async server components in one bundle):\n// import { NextAdminPage } from \"@vexcms/next/server\" ← async server component\n// import { NextAdminLayout } from \"@vexcms/next/client\" ← \"use client\" component\n//\n// 2. Config / type re-exports (everything below). Re-exports `@vexcms/react`'s\n// HKT-bound config functions and types transitively so Next consumers can\n// import every config function from a single package:\n//\n// import {\n// defineConfig,\n// defineCollection,\n// relationship,\n// text, number, select, date, url, checkbox,\n// } from \"@vexcms/next\";\n//\n// `admin.components.preview` slots remain typed as React `ComponentType<P>`\n// via `ReactHKT` because @vexcms/react bound it before re-exporting. See\n// spec 22 Decision 15 for the architectural rationale.\n\n// Server / client surface\nexport * from \"./NextAdminPage\";\nexport * from \"./NextAdminLayout\";\n\n// Config functions — HKT-bound wrappers from @vexcms/react\nexport { relationship, defineCollection } from \"@vexcms/react\";\n\n// Field config functions without component slots (pure pass-through)\nexport {\n text,\n number,\n select,\n date,\n url,\n checkbox,\n defineConfig,\n} from \"@vexcms/react\";\n\n// Public types — HKT-bound where applicable.\n// `*FieldInput` core config types intentionally not re-exported (they collide\n// with React `*FieldInput` component names; users get them via inference\n// through the config functions).\nexport type {\n AdminUser,\n // HKT-bound (admin.components.preview = React ComponentType)\n RelationshipFieldInput,\n RelationshipField,\n AdminCollectionConfigInput,\n CollectionConfigInput,\n CollectionConfig,\n // Resolved field types\n TextField,\n NumberField,\n SelectField,\n DateField,\n UrlField,\n CheckboxField,\n // Shared types\n AdminField,\n CollectionSlug,\n VexDocument,\n VexConfig,\n VexClientConfig,\n VexClientConfigInput,\n VexServerConfigInput,\n RelationshipPreviewProps,\n} from \"@vexcms/react\";\n\n// API functions are NOT re-exported from this barrel because @vexcms/next's\n// root index.ts is imported by server components (NextAdminPage). Pulling\n// @vexcms/core/client here would transitively import @convex-dev/react-query\n// which uses createContext — crashing RSC.\n//\n// Import directly from the correct environment-specific path instead:\n//\n// import { find, get, search } from \"@vexcms/core/client\"; ← React components\n// import { find, get, search } from \"@vexcms/core/server\"; ← Convex handlers\n\nexport type {\n RelationshipKeysOf,\n TextKeysOf,\n SortableKeysOf,\n RelationshipTargetOf,\n Populated,\n PopulateShape,\n} from \"@vexcms/core\";\n\n// Cache / SEO surface — server-only, no client context, so these are safe in\n// this RSC-imported root barrel. Also reachable via the \"@vexcms/next/cache\"\n// and \"@vexcms/next/seo\" subpaths.\nexport { createVexServerClient } from \"./cache/createVexServerClient\";\nexport type { VexServerClientOptions, VexServerClient } from \"./cache/types\";\nexport { createVexRobots } from \"./seo/createVexRobots\";\nexport { createVexSitemap } from \"./seo/createVexSitemap\";\nexport { vexMetadata } from \"./seo/vexMetadata\";\nexport { vexStaticParams } from \"./seo/vexStaticParams\";\nexport { createVexRevalidateRoute } from \"./cache/createVexRevalidateRoute\";\nexport type { CreateVexRevalidateRouteProps } from \"./cache/createVexRevalidateRoute\";\nexport type {\n} from \"./cache/types\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,SAAS,cAAc,wBAAwB;AAG/C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vexcms/next",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.19",
4
4
  "description": "Next.js adapter and admin routes for VexCMS",
5
5
  "keywords": [
6
6
  "cms",
@@ -66,8 +66,8 @@
66
66
  },
67
67
  "dependencies": {
68
68
  "lucide-react": "0.577.0",
69
- "@vexcms/react": "0.1.0-alpha.16",
70
- "@vexcms/core": "0.1.0-alpha.16"
69
+ "@vexcms/react": "0.1.0-alpha.19",
70
+ "@vexcms/core": "0.1.0-alpha.19"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@convex-dev/react-query": "0.1.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/NextAdminPage.tsx"],"sourcesContent":["import { fetchQuery } from \"convex/nextjs\";\nimport { vexConvexApi } from \"@vexcms/core\";\nimport type { CollectionSlug, PaginationResult, VexConfig, VexMediaDocument } from \"@vexcms/core\";\nimport {\n DashboardView,\n CollectionListView,\n CollectionEditView,\n MediaCollectionEditView,\n MediaCollectionListView,\n GlobalsListView,\n GlobalEditView,\n} from \"@vexcms/react\";\nimport { sanitizeConfigForClient } from \"@vexcms/core\";\n\n/**\n * VexCMS admin page server component for Next.js.\n *\n * An `async` server component that routes by the `[[...slug]]` catch-all\n * params, prefetches Convex data via `fetchQuery`, and renders the correct\n * view component. Does **not** include a layout wrapper — `VexAdminLayout`\n * in `app/admin/layout.tsx` owns the persistent shell.\n *\n * **Route mapping:**\n * | `path` array | View |\n * |---|---|\n * | `[]` or undefined | `DashboardView` |\n * | `[collectionSlug]` | `CollectionListView` with preloaded docs |\n * | `[collectionSlug, \"new\"]` | `CollectionEditView` (empty form) |\n * | `[collectionSlug, documentId]` | `CollectionEditView` with preloaded doc |\n *\n * @param props - Component props\n * @param props.config - The resolved VexCMS configuration from `vex.config.ts`\n * @param props.params - Next.js 15 async params `{ path?: string[] }`\n * @returns The appropriate admin view for the current URL path.\n *\n * @example\n * ```tsx\n * // app/admin/[[...slug]]/page.tsx\n * import { VexAdminPage } from \"@vexcms/next\";\n * import config from \"../../../../vex.config\";\n *\n * export default function AdminPage({\n * params,\n * }: {\n * params: Promise<{ path?: string[] }>;\n * }) {\n * return <VexAdminPage config={config} params={params} />;\n * }\n * ```\n */\nexport async function NextAdminPage(props: {\n config: VexConfig;\n params: Promise<{ path?: string[] }>;\n token?: string;\n}) {\n const { path = [] } = await props.params;\n const [collectionSlug, documentId] = path;\n\n // Sanitize config for client components (strips storageAdapters, recursively sanitizes mediaCollections)\n const clientConfig = sanitizeConfigForClient(props.config);\n\n if (!collectionSlug) {\n return <DashboardView config={clientConfig} />;\n }\n\n if (collectionSlug === \"globals\") {\n if (!documentId) {\n return <GlobalsListView config={clientConfig} />;\n }\n // Validate the route param against the registered globals first — the\n // found config's `slug` carries the narrowed GlobalSlug type, so no cast\n // of the raw URL segment is needed downstream.\n const globalConfig = clientConfig.globals.find((g) => g.slug === documentId);\n if (!globalConfig) {\n return (\n <div>\n <p className=\"text-muted-foreground p-6\">Global &quot;{documentId}&quot; not found.</p>\n <p>TODO: add not found view</p>\n </div>\n );\n }\n const global = await fetchQuery(\n vexConvexApi.globals.get,\n { slug: globalConfig.slug },\n props.token ? { token: props.token } : undefined,\n );\n return <GlobalEditView global={globalConfig} initialData={global} />;\n }\n\n const collection = clientConfig.collections.find((c) => c.slug === collectionSlug);\n const mediaCollection = clientConfig.mediaCollections.find((mc) => mc.slug === collectionSlug);\n\n if (!collection && !mediaCollection) {\n return (\n <div>\n <p className=\"text-muted-foreground p-6\">\n Collection &quot;{collectionSlug}&quot; not found.\n </p>\n <p>TODO: add not found view</p>\n </div>\n );\n }\n\n if (mediaCollection && documentId) {\n const initialData = await fetchQuery(\n vexConvexApi.get,\n {\n id: documentId,\n collection: mediaCollection.slug,\n },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <MediaCollectionEditView\n collection={mediaCollection}\n documentId={documentId}\n initialData={initialData as VexMediaDocument | null}\n />\n );\n }\n\n if (mediaCollection) {\n const numItems = Math.max(\n mediaCollection.admin.table.serverPageSize,\n mediaCollection.admin.table.defaultPageSize,\n );\n const initialData = await fetchQuery(\n vexConvexApi.find,\n {\n collection: collectionSlug as CollectionSlug,\n paginationOpts: {\n numItems,\n totalDocs: true,\n cursor: null,\n },\n },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <MediaCollectionListView\n collection={mediaCollection}\n initialData={initialData as PaginationResult<VexMediaDocument>}\n />\n );\n }\n\n if (collection && documentId) {\n const initialData = await fetchQuery(\n vexConvexApi.get,\n {\n id: documentId,\n collection: collection.slug,\n },\n props.token ? { token: props.token } : undefined,\n );\n return (\n <CollectionEditView\n collection={collection}\n documentId={documentId}\n initialData={initialData}\n />\n );\n }\n\n if (!collection) {\n throw new Error(\"invalid collection slug\");\n }\n\n const numItems = Math.max(\n collection.admin.table.serverPageSize,\n collection.admin.table.defaultPageSize,\n );\n const initialData = await fetchQuery(\n vexConvexApi.findPaginated,\n {\n collection: collectionSlug as CollectionSlug,\n depth: 1,\n paginationOpts: {\n cursor: null,\n numItems,\n totalDocs: true,\n },\n },\n props.token ? { token: props.token } : undefined,\n );\n return <CollectionListView collection={collection} initialData={initialData} />;\n}\n"],"mappings":"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","names":["initialData","numItems"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/NextAdminLayout.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { VexConfig } from \"@vexcms/core\";\nimport { sanitizeConfigForClient } from \"@vexcms/core\";\nimport type { AdminUser } from \"@vexcms/react\";\nimport { NextAdminLayoutClient } from \"./NextAdminLayoutClient\";\nimport { cookies } from \"next/headers\";\n\n/**\n * Next.js admin layout shell for VexCMS.\n *\n * A **server component** that sits on the server→client boundary for the admin\n * panel. It sanitizes the resolved `vex.config` with `sanitizeConfigForClient`\n * — stripping storage adapter class instances, functions, and React components\n * — *before* the config is handed to the client leaf `NextAdminLayoutClient`.\n *\n * This placement is the whole point: React serializes props when a server\n * component renders a client component, so the sanitize **must** happen on the\n * server side of that edge. Sanitizing inside the client component would run\n * too late — the raw config would already have failed to serialize.\n *\n * The client leaf handles everything that needs the browser (`usePathname`,\n * `NuqsAdapter`, `NextLink`/`NextImage`); this wrapper does no client work.\n *\n * Render it from your admin `layout.tsx`. Because this is a server component,\n * your `layout.tsx` may itself be an `async` server component (e.g. to run auth\n * checks) and pass the **raw** `vex.config` straight through — no sanitize\n * needed on the application side.\n *\n * @param props - Layout props\n * @param props.config - The resolved VexCMS config from `vex.config.ts` (raw)\n * @param props.children - The page content from `[[...slug]]/page.tsx`\n * @param props.user - Current user for the admin shell\n * @returns The rendered admin shell, wrapping `children` in the sanitized-config\n * client boundary ({@link NextAdminLayoutClient}).\n * @example\n * ```tsx\n * // app/admin/layout.tsx\n * import { NextAdminLayout } from \"@vexcms/next/client\";\n * import config from \"~/vex.config\";\n * import { getCurrentUser } from \"~/auth/serverUtils\";\n *\n * export default async function AdminLayout({ children }: { children: ReactNode }) {\n * const user = await getCurrentUser();\n * return (\n * <NextAdminLayout config={config} user={user ?? undefined}>\n * {children}\n * </NextAdminLayout>\n * );\n * }\n * ```\n */\nexport async function NextAdminLayout(props: {\n config: VexConfig;\n children: ReactNode;\n user?: AdminUser;\n organization?: Record<string, unknown>;\n}) {\n // Sanitize on the SERVER side of the server→client boundary, before the\n // config is serialized as a prop into the client leaf. This removes the\n // storage adapter class instances that would otherwise crash RSC\n // serialization (\"Classes or null prototypes are not supported\").\n const clientConfig = sanitizeConfigForClient(props.config);\n const cookieStore = await cookies();\n const sidebarOpen = String(cookieStore.get(\"sidebar_state\")?.value) === \"true\";\n\n return (\n <NextAdminLayoutClient\n config={clientConfig}\n user={props.user}\n organization={props.organization}\n sidebarOpen={sidebarOpen}\n >\n {props.children}\n </NextAdminLayoutClient>\n );\n}\n"],"mappings":";AAEA,SAAS,+BAA+B;AAExC,SAAS,6BAA6B;AACtC,SAAS,eAAe;AA6DpB;AAfJ,eAAsB,gBAAgB,OAKnC;AAKD,QAAM,eAAe,wBAAwB,MAAM,MAAM;AACzD,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,cAAc,OAAO,YAAY,IAAI,eAAe,GAAG,KAAK,MAAM;AAExE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ;AAAA,MACR,MAAM,MAAM;AAAA,MACZ,cAAc,MAAM;AAAA,MACpB;AAAA,MAEC,gBAAM;AAAA;AAAA,EACT;AAEJ;","names":[]}