@cmssy/next 3.0.0 → 4.0.0

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/dist/index.cjs CHANGED
@@ -255,6 +255,12 @@ async function resolveEditorRequest(query, draftSecret) {
255
255
  return cmssySecretsMatch(provided, draftSecret);
256
256
  }
257
257
  function createCmssyPage(config, blocks, options) {
258
+ return buildCmssyPageRenderer(config, blocks, options, false);
259
+ }
260
+ function createCmssyEditPage(config, blocks, options) {
261
+ return buildCmssyPageRenderer(config, blocks, options, true);
262
+ }
263
+ function buildCmssyPageRenderer(config, blocks, options, editRoute) {
258
264
  if (!Array.isArray(blocks)) {
259
265
  throw new Error(
260
266
  "cmssy: createCmssyPage(config, blocks) requires a blocks array \u2014 pass your defineBlock(...) array"
@@ -274,8 +280,14 @@ function createCmssyPage(config, blocks, options) {
274
280
  }) {
275
281
  const path = fixedPath ?? (params ? (await params).path ?? void 0 : void 0);
276
282
  const { isEnabled } = await headers.draftMode();
277
- const query = searchParams ? await searchParams : {};
278
- const editorActive = await resolveEditorRequest(query, config.draftSecret);
283
+ let editorActive = false;
284
+ if (editRoute) {
285
+ const query = searchParams ? await searchParams : {};
286
+ editorActive = await resolveEditorRequest(query, config.draftSecret);
287
+ if (!editorActive) {
288
+ navigation.notFound();
289
+ }
290
+ }
279
291
  const editMode = isEnabled || editorActive;
280
292
  const devAllowed = isDevelopment() && Boolean(config.devToken?.trim());
281
293
  let locale;
@@ -386,6 +398,23 @@ function resolveBridgeOrigin(editorOrigin) {
386
398
  }
387
399
  return origins.length === 1 ? origins[0] : origins;
388
400
  }
401
+ var CMSSY_EDIT_PATH_PREFIX = "/__cmssy/edit";
402
+ async function cmssyEditRewrite(request2, config) {
403
+ const { pathname, searchParams } = request2.nextUrl;
404
+ if (pathname.startsWith(CMSSY_EDIT_PATH_PREFIX)) return null;
405
+ if (!searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) return null;
406
+ const provided = searchParams.get(CMSSY_SECRET_QUERY_PARAM);
407
+ if (!provided || !config.draftSecret) return null;
408
+ if (!await cmssySecretsMatch(provided, config.draftSecret)) return null;
409
+ const url = request2.nextUrl.clone();
410
+ url.pathname = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}`;
411
+ return server.NextResponse.rewrite(url);
412
+ }
413
+ function createCmssyEditMiddleware(config) {
414
+ return async function cmssyEditMiddleware(request2) {
415
+ return await cmssyEditRewrite(request2, config) ?? server.NextResponse.next();
416
+ };
417
+ }
389
418
  function DefaultNotFound() {
390
419
  return /* @__PURE__ */ jsxRuntime.jsxs(
391
420
  "main",
@@ -698,7 +727,12 @@ async function splitCmssyLocale(config, path) {
698
727
  const siteLocales = await react.resolveSiteLocales(config);
699
728
  return react.splitLocaleFromPath(path, siteLocales);
700
729
  }
701
- async function getCmssyLocale(config) {
730
+ async function getCmssyLocale(config, options) {
731
+ if (options?.path !== void 0) {
732
+ const siteLocales = await react.resolveSiteLocales(config);
733
+ const segments = Array.isArray(options.path) ? options.path.filter(Boolean) : options.path.split("/").filter(Boolean);
734
+ return react.splitLocaleFromPath(segments, siteLocales).locale;
735
+ }
702
736
  const { headers: headers3 } = await import('next/headers');
703
737
  const headerList = await headers3();
704
738
  const fromHeader = headerList.get(CMSSY_LOCALE_HEADER);
@@ -1958,6 +1992,7 @@ Object.defineProperty(exports, "resolveApiUrl", {
1958
1992
  });
1959
1993
  exports.CMSSY_CART_COOKIE = CMSSY_CART_COOKIE;
1960
1994
  exports.CMSSY_EDIT_HEADER = CMSSY_EDIT_HEADER;
1995
+ exports.CMSSY_EDIT_PATH_PREFIX = CMSSY_EDIT_PATH_PREFIX;
1961
1996
  exports.CMSSY_EDIT_QUERY_PARAM = CMSSY_EDIT_QUERY_PARAM;
1962
1997
  exports.CMSSY_LOCALE_HEADER = CMSSY_LOCALE_HEADER;
1963
1998
  exports.CMSSY_SECRET_QUERY_PARAM = CMSSY_SECRET_QUERY_PARAM;
@@ -1969,9 +2004,12 @@ exports.applyCmssyCsp = applyCmssyCsp;
1969
2004
  exports.assertAuthConfig = assertAuthConfig;
1970
2005
  exports.buildCmssyMetadata = buildCmssyMetadata;
1971
2006
  exports.cmssyCspHeaders = cmssyCspHeaders;
2007
+ exports.cmssyEditRewrite = cmssyEditRewrite;
1972
2008
  exports.createCmssyAuthMiddleware = createCmssyAuthMiddleware;
1973
2009
  exports.createCmssyAuthRoute = createCmssyAuthRoute;
1974
2010
  exports.createCmssyCartRoute = createCmssyCartRoute;
2011
+ exports.createCmssyEditMiddleware = createCmssyEditMiddleware;
2012
+ exports.createCmssyEditPage = createCmssyEditPage;
1975
2013
  exports.createCmssyLocaleMiddleware = createCmssyLocaleMiddleware;
1976
2014
  exports.createCmssyNotFound = createCmssyNotFound;
1977
2015
  exports.createCmssyOrdersRoute = createCmssyOrdersRoute;
package/dist/index.d.cts CHANGED
@@ -5,8 +5,8 @@ export { DEFAULT_CMSSY_API_URL, FieldCondition, FieldConditionGroup, FieldCondit
5
5
  import { EditBridgeConfig } from '@cmssy/react/client';
6
6
  import { CmssyAuthConfig, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, FetchProductOptions, CmssyProduct, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, CmssyOrder, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
7
7
  export { CmssyAuthConfig, CmssyProductPage, CmssySessionPayload, CmssySessionUser, CmssyStockState, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, MyOrdersResult, SessionCookieOptions, VerifyCmssyWebhookOptions } from '@cmssy/types';
8
- import { MetadataRoute, Metadata } from 'next';
9
8
  import { NextRequest, NextResponse } from 'next/server';
9
+ import { MetadataRoute, Metadata } from 'next';
10
10
 
11
11
  declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
12
12
  declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
@@ -88,7 +88,51 @@ interface CatchAllProps {
88
88
  params?: Promise<CatchAllParams>;
89
89
  searchParams?: Promise<SearchParams>;
90
90
  }
91
+ /**
92
+ * Public catch-all page. Statically renderable: it never reads
93
+ * `searchParams` or `headers()` - awaiting either forces every route
94
+ * dynamic and kills ISR (CMS-952). Draft-mode preview (the authenticated
95
+ * /api/draft cookie) still works per-request via `draftMode()`, which is
96
+ * static-safe, and renders draft CONTENT without the editor (CMS-948).
97
+ * The editor-iframe flow (`?cmssyEdit=1&cmssySecret=...`) is served by
98
+ * the middleware rewrite (`cmssyEditRewrite`) onto the dynamic edit route
99
+ * mounted with `createCmssyEditPage`.
100
+ */
91
101
  declare function createCmssyPage(config: CmssyNextConfig, blocks: BlockDefinition[], options?: CreateCmssyPageOptions): ({ params, searchParams, }: CatchAllProps) => Promise<react_jsx_runtime.JSX.Element>;
102
+ /**
103
+ * Editor page for the middleware-rewritten edit route
104
+ * (`app/__cmssy/edit/[[...path]]/page.tsx`, `dynamic = "force-dynamic"`).
105
+ * Re-verifies the `cmssyEdit` + `cmssySecret` pair itself, so a direct hit
106
+ * on the route path (bypassing the middleware) cannot mount the editor.
107
+ */
108
+ declare function createCmssyEditPage(config: CmssyNextConfig, blocks: BlockDefinition[], options?: CreateCmssyPageOptions): ({ params, searchParams, }: CatchAllProps) => Promise<react_jsx_runtime.JSX.Element>;
109
+
110
+ /**
111
+ * Where the middleware rewrites editor-preview traffic. Mount the matching
112
+ * dynamic route in the consumer app:
113
+ *
114
+ * app/__cmssy/edit/[[...path]]/page.tsx
115
+ * export const dynamic = "force-dynamic";
116
+ * export default createCmssyEditPage(cmssy, blocks, { editor: Editor });
117
+ */
118
+ declare const CMSSY_EDIT_PATH_PREFIX = "/__cmssy/edit";
119
+ /**
120
+ * Rewrite VERIFIED editor requests (`cmssyEdit=1` + a `cmssySecret` matching
121
+ * the site's draft secret, CMS-948) onto the dedicated dynamic edit route, so
122
+ * the public catch-all can stay fully static. A static page never sees its
123
+ * query string - this rewrite is what makes the editor iframe work at all
124
+ * once ISR is on. Draft-mode preview (the authenticated /api/draft cookie) is
125
+ * NOT rewritten: it renders draft content on the public route via
126
+ * `draftMode()`, without the editor. Returns null for normal traffic so it
127
+ * composes with other middleware.
128
+ */
129
+ declare function cmssyEditRewrite(request: NextRequest, config: {
130
+ draftSecret: string;
131
+ }): Promise<NextResponse | null>;
132
+ /** Standalone middleware when the consumer has no other middleware to compose. */
133
+ declare function createCmssyEditMiddleware(config: {
134
+ draftSecret: string;
135
+ }): (request: NextRequest) => Promise<NextResponse>;
92
136
 
93
137
  interface CreateCmssyNotFoundOptions {
94
138
  /**
@@ -220,7 +264,15 @@ declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | un
220
264
  locale: string;
221
265
  path: string[] | undefined;
222
266
  }>;
223
- declare function getCmssyLocale(config: CmssyClientConfig): Promise<string>;
267
+ /**
268
+ * Resolve the active locale. Pass `options.path` (route params) wherever
269
+ * possible - that path is static-safe. The no-argument form falls back to the
270
+ * `x-cmssy-locale` request header, which reads `headers()` and forces the
271
+ * calling route into dynamic rendering.
272
+ */
273
+ declare function getCmssyLocale(config: CmssyClientConfig, options?: {
274
+ path?: string | string[];
275
+ }): Promise<string>;
224
276
 
225
277
  /**
226
278
  * Resolves the active locale from a pathname's leading segment. Uses the
@@ -304,4 +356,4 @@ declare class CmssyWebhookError extends Error {
304
356
  */
305
357
  declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
306
358
 
307
- export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
359
+ export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_PATH_PREFIX, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, cmssyEditRewrite, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyEditMiddleware, createCmssyEditPage, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/dist/index.d.ts CHANGED
@@ -5,8 +5,8 @@ export { DEFAULT_CMSSY_API_URL, FieldCondition, FieldConditionGroup, FieldCondit
5
5
  import { EditBridgeConfig } from '@cmssy/react/client';
6
6
  import { CmssyAuthConfig, CmssySessionPayload, SessionCookieOptions, CmssySessionUser, FetchProductOptions, CmssyProduct, FetchProductsOptions, CmssyProductPage, FetchOrderByTokenOptions, CmssyOrder, VerifyCmssyWebhookOptions, CmssyWebhookEvent } from '@cmssy/types';
7
7
  export { CmssyAuthConfig, CmssyProductPage, CmssySessionPayload, CmssySessionUser, CmssyStockState, CmssyWebhookEvent, CmssyWebhookOrder, FetchOrderByTokenOptions, FetchProductOptions, FetchProductsOptions, MyOrdersResult, SessionCookieOptions, VerifyCmssyWebhookOptions } from '@cmssy/types';
8
- import { MetadataRoute, Metadata } from 'next';
9
8
  import { NextRequest, NextResponse } from 'next/server';
9
+ import { MetadataRoute, Metadata } from 'next';
10
10
 
11
11
  declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
12
12
  declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
@@ -88,7 +88,51 @@ interface CatchAllProps {
88
88
  params?: Promise<CatchAllParams>;
89
89
  searchParams?: Promise<SearchParams>;
90
90
  }
91
+ /**
92
+ * Public catch-all page. Statically renderable: it never reads
93
+ * `searchParams` or `headers()` - awaiting either forces every route
94
+ * dynamic and kills ISR (CMS-952). Draft-mode preview (the authenticated
95
+ * /api/draft cookie) still works per-request via `draftMode()`, which is
96
+ * static-safe, and renders draft CONTENT without the editor (CMS-948).
97
+ * The editor-iframe flow (`?cmssyEdit=1&cmssySecret=...`) is served by
98
+ * the middleware rewrite (`cmssyEditRewrite`) onto the dynamic edit route
99
+ * mounted with `createCmssyEditPage`.
100
+ */
91
101
  declare function createCmssyPage(config: CmssyNextConfig, blocks: BlockDefinition[], options?: CreateCmssyPageOptions): ({ params, searchParams, }: CatchAllProps) => Promise<react_jsx_runtime.JSX.Element>;
102
+ /**
103
+ * Editor page for the middleware-rewritten edit route
104
+ * (`app/__cmssy/edit/[[...path]]/page.tsx`, `dynamic = "force-dynamic"`).
105
+ * Re-verifies the `cmssyEdit` + `cmssySecret` pair itself, so a direct hit
106
+ * on the route path (bypassing the middleware) cannot mount the editor.
107
+ */
108
+ declare function createCmssyEditPage(config: CmssyNextConfig, blocks: BlockDefinition[], options?: CreateCmssyPageOptions): ({ params, searchParams, }: CatchAllProps) => Promise<react_jsx_runtime.JSX.Element>;
109
+
110
+ /**
111
+ * Where the middleware rewrites editor-preview traffic. Mount the matching
112
+ * dynamic route in the consumer app:
113
+ *
114
+ * app/__cmssy/edit/[[...path]]/page.tsx
115
+ * export const dynamic = "force-dynamic";
116
+ * export default createCmssyEditPage(cmssy, blocks, { editor: Editor });
117
+ */
118
+ declare const CMSSY_EDIT_PATH_PREFIX = "/__cmssy/edit";
119
+ /**
120
+ * Rewrite VERIFIED editor requests (`cmssyEdit=1` + a `cmssySecret` matching
121
+ * the site's draft secret, CMS-948) onto the dedicated dynamic edit route, so
122
+ * the public catch-all can stay fully static. A static page never sees its
123
+ * query string - this rewrite is what makes the editor iframe work at all
124
+ * once ISR is on. Draft-mode preview (the authenticated /api/draft cookie) is
125
+ * NOT rewritten: it renders draft content on the public route via
126
+ * `draftMode()`, without the editor. Returns null for normal traffic so it
127
+ * composes with other middleware.
128
+ */
129
+ declare function cmssyEditRewrite(request: NextRequest, config: {
130
+ draftSecret: string;
131
+ }): Promise<NextResponse | null>;
132
+ /** Standalone middleware when the consumer has no other middleware to compose. */
133
+ declare function createCmssyEditMiddleware(config: {
134
+ draftSecret: string;
135
+ }): (request: NextRequest) => Promise<NextResponse>;
92
136
 
93
137
  interface CreateCmssyNotFoundOptions {
94
138
  /**
@@ -220,7 +264,15 @@ declare function splitCmssyLocale(config: CmssyClientConfig, path: string[] | un
220
264
  locale: string;
221
265
  path: string[] | undefined;
222
266
  }>;
223
- declare function getCmssyLocale(config: CmssyClientConfig): Promise<string>;
267
+ /**
268
+ * Resolve the active locale. Pass `options.path` (route params) wherever
269
+ * possible - that path is static-safe. The no-argument form falls back to the
270
+ * `x-cmssy-locale` request header, which reads `headers()` and forces the
271
+ * calling route into dynamic rendering.
272
+ */
273
+ declare function getCmssyLocale(config: CmssyClientConfig, options?: {
274
+ path?: string | string[];
275
+ }): Promise<string>;
224
276
 
225
277
  /**
226
278
  * Resolves the active locale from a pathname's leading segment. Uses the
@@ -304,4 +356,4 @@ declare class CmssyWebhookError extends Error {
304
356
  */
305
357
  declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
306
358
 
307
- export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
359
+ export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_PATH_PREFIX, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, cmssyEditRewrite, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyEditMiddleware, createCmssyEditPage, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/dist/index.js CHANGED
@@ -254,6 +254,12 @@ async function resolveEditorRequest(query, draftSecret) {
254
254
  return cmssySecretsMatch(provided, draftSecret);
255
255
  }
256
256
  function createCmssyPage(config, blocks, options) {
257
+ return buildCmssyPageRenderer(config, blocks, options, false);
258
+ }
259
+ function createCmssyEditPage(config, blocks, options) {
260
+ return buildCmssyPageRenderer(config, blocks, options, true);
261
+ }
262
+ function buildCmssyPageRenderer(config, blocks, options, editRoute) {
257
263
  if (!Array.isArray(blocks)) {
258
264
  throw new Error(
259
265
  "cmssy: createCmssyPage(config, blocks) requires a blocks array \u2014 pass your defineBlock(...) array"
@@ -273,8 +279,14 @@ function createCmssyPage(config, blocks, options) {
273
279
  }) {
274
280
  const path = fixedPath ?? (params ? (await params).path ?? void 0 : void 0);
275
281
  const { isEnabled } = await draftMode();
276
- const query = searchParams ? await searchParams : {};
277
- const editorActive = await resolveEditorRequest(query, config.draftSecret);
282
+ let editorActive = false;
283
+ if (editRoute) {
284
+ const query = searchParams ? await searchParams : {};
285
+ editorActive = await resolveEditorRequest(query, config.draftSecret);
286
+ if (!editorActive) {
287
+ notFound();
288
+ }
289
+ }
278
290
  const editMode = isEnabled || editorActive;
279
291
  const devAllowed = isDevelopment() && Boolean(config.devToken?.trim());
280
292
  let locale;
@@ -385,6 +397,23 @@ function resolveBridgeOrigin(editorOrigin) {
385
397
  }
386
398
  return origins.length === 1 ? origins[0] : origins;
387
399
  }
400
+ var CMSSY_EDIT_PATH_PREFIX = "/__cmssy/edit";
401
+ async function cmssyEditRewrite(request2, config) {
402
+ const { pathname, searchParams } = request2.nextUrl;
403
+ if (pathname.startsWith(CMSSY_EDIT_PATH_PREFIX)) return null;
404
+ if (!searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) return null;
405
+ const provided = searchParams.get(CMSSY_SECRET_QUERY_PARAM);
406
+ if (!provided || !config.draftSecret) return null;
407
+ if (!await cmssySecretsMatch(provided, config.draftSecret)) return null;
408
+ const url = request2.nextUrl.clone();
409
+ url.pathname = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}`;
410
+ return NextResponse.rewrite(url);
411
+ }
412
+ function createCmssyEditMiddleware(config) {
413
+ return async function cmssyEditMiddleware(request2) {
414
+ return await cmssyEditRewrite(request2, config) ?? NextResponse.next();
415
+ };
416
+ }
388
417
  function DefaultNotFound() {
389
418
  return /* @__PURE__ */ jsxs(
390
419
  "main",
@@ -697,7 +726,12 @@ async function splitCmssyLocale(config, path) {
697
726
  const siteLocales = await resolveSiteLocales(config);
698
727
  return splitLocaleFromPath(path, siteLocales);
699
728
  }
700
- async function getCmssyLocale(config) {
729
+ async function getCmssyLocale(config, options) {
730
+ if (options?.path !== void 0) {
731
+ const siteLocales = await resolveSiteLocales(config);
732
+ const segments = Array.isArray(options.path) ? options.path.filter(Boolean) : options.path.split("/").filter(Boolean);
733
+ return splitLocaleFromPath(segments, siteLocales).locale;
734
+ }
701
735
  const { headers: headers3 } = await import('next/headers');
702
736
  const headerList = await headers3();
703
737
  const fromHeader = headerList.get(CMSSY_LOCALE_HEADER);
@@ -1943,4 +1977,4 @@ function verifyCmssyWebhook(options) {
1943
1977
  return parsed;
1944
1978
  }
1945
1979
 
1946
- export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
1980
+ export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_PATH_PREFIX, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, cmssyEditRewrite, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyEditMiddleware, createCmssyEditPage, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/next",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Next.js App Router bindings for cmssy headless sites (createCmssyPage + draft preview)",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -41,7 +41,7 @@
41
41
  "dist"
42
42
  ],
43
43
  "peerDependencies": {
44
- "@cmssy/react": "^3.0.0",
44
+ "@cmssy/react": "^4.0.0",
45
45
  "next": ">=15",
46
46
  "react": "^18.2.0 || ^19.0.0",
47
47
  "react-dom": "^18.2.0 || ^19.0.0"
@@ -55,7 +55,7 @@
55
55
  "tsup": "^8.3.0",
56
56
  "typescript": "^5.6.0",
57
57
  "vitest": "^2.1.0",
58
- "@cmssy/react": "3.0.0"
58
+ "@cmssy/react": "4.0.0"
59
59
  },
60
60
  "dependencies": {
61
61
  "jose": "^6.2.3",