@cmssy/next 10.9.1 → 10.10.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.
@@ -74,8 +74,11 @@ function createCmssyProxy(config, options = {}) {
74
74
  for (const write of writes) {
75
75
  response.cookies.set(write.name, write.value, {
76
76
  ...DEFAULT_COOKIE_OPTIONS,
77
- ...write.value ? {} : { maxAge: 0 },
78
- ...write.options
77
+ ...write.options,
78
+ // Deletion wins over the caller's options. They will be the same
79
+ // options the cookie was written with, maxAge included, and honoring
80
+ // that here would keep an emptied cookie alive instead of removing it.
81
+ ...write.value ? {} : { maxAge: 0 }
79
82
  });
80
83
  }
81
84
  return response;
@@ -73,8 +73,11 @@ function createCmssyProxy(config, options = {}) {
73
73
  for (const write of writes) {
74
74
  response.cookies.set(write.name, write.value, {
75
75
  ...DEFAULT_COOKIE_OPTIONS,
76
- ...write.value ? {} : { maxAge: 0 },
77
- ...write.options
76
+ ...write.options,
77
+ // Deletion wins over the caller's options. They will be the same
78
+ // options the cookie was written with, maxAge included, and honoring
79
+ // that here would keep an emptied cookie alive instead of removing it.
80
+ ...write.value ? {} : { maxAge: 0 }
78
81
  });
79
82
  }
80
83
  return response;
package/dist/server.cjs CHANGED
@@ -9,6 +9,7 @@ var internalServer = require('@cmssy/react/internal-server');
9
9
  var internal = require('@cmssy/core/internal');
10
10
  var core = require('@cmssy/core');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
+ var locale = require('@cmssy/core/internal/locale');
12
13
 
13
14
  // src/server.ts
14
15
  function hasEditFlag(value) {
@@ -249,7 +250,71 @@ async function isCmssyEditMode() {
249
250
  const h = await headers.headers();
250
251
  return h.get(core.CMSSY_EDIT_HEADER) === "1";
251
252
  }
253
+ async function CmssyLayoutSlot({
254
+ config,
255
+ blocks,
256
+ position,
257
+ path,
258
+ page = "/",
259
+ editable: Editable,
260
+ appContext
261
+ }) {
262
+ const editMode = await isCmssyEditMode();
263
+ const [groups, siteLocales] = await Promise.all([
264
+ internal.fetchLayouts(config, page, {
265
+ previewSecret: editMode ? config.draftSecret : void 0
266
+ }),
267
+ locale.resolveSiteLocales(config)
268
+ ]);
269
+ const locale$1 = path ? locale.splitLocaleFromPath(path, siteLocales).locale : await localeFromHeader(siteLocales.defaultLocale);
270
+ if (!editMode) {
271
+ return /* @__PURE__ */ jsxRuntime.jsx(
272
+ react.CmssyServerLayout,
273
+ {
274
+ groups,
275
+ blocks,
276
+ position,
277
+ locale: locale$1,
278
+ defaultLocale: siteLocales.defaultLocale,
279
+ enabledLocales: siteLocales.locales,
280
+ config,
281
+ appContext
282
+ }
283
+ );
284
+ }
285
+ const origin = core.resolveEditorOrigin(config.editorOrigin);
286
+ const editorData = await react.resolveEditorLayoutBlockData({
287
+ groups,
288
+ blocks,
289
+ position,
290
+ locale: locale$1,
291
+ defaultLocale: siteLocales.defaultLocale,
292
+ enabledLocales: siteLocales.locales,
293
+ isPreview: true,
294
+ config,
295
+ appContext
296
+ });
297
+ return /* @__PURE__ */ jsxRuntime.jsx(
298
+ Editable,
299
+ {
300
+ groups,
301
+ position,
302
+ locale: locale$1,
303
+ defaultLocale: siteLocales.defaultLocale,
304
+ enabledLocales: siteLocales.locales,
305
+ edit: { editorOrigin: (Array.isArray(origin) ? origin[0] : origin) ?? "" },
306
+ data: editorData.data,
307
+ resolvedContent: editorData.content,
308
+ appContext
309
+ }
310
+ );
311
+ }
312
+ async function localeFromHeader(defaultLocale) {
313
+ const { headers: headers3 } = await import('next/headers');
314
+ return (await headers3()).get(locale.CMSSY_LOCALE_HEADER) || defaultLocale;
315
+ }
252
316
 
317
+ exports.CmssyLayoutSlot = CmssyLayoutSlot;
253
318
  exports.createCmssyEditPage = createCmssyEditPage;
254
319
  exports.createCmssyPage = createCmssyPage;
255
320
  exports.createDraftRoute = createDraftRoute;
package/dist/server.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { C as CmssyAppContext, a as CmssyEditorProps, b as CreateCmssyPageOptions, c as createCmssyEditPage, d as createCmssyPage } from './index-B64zk2X7.cjs';
2
- import { CmssyConfig } from '@cmssy/core';
3
- import 'react/jsx-runtime';
4
- import 'react';
5
- import '@cmssy/react';
2
+ import { CmssyConfig, LayoutPosition } from '@cmssy/core';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ComponentType } from 'react';
5
+ import { BlockDefinition, CmssyLayoutGroup } from '@cmssy/react';
6
6
  import '@cmssy/react/client';
7
7
 
8
8
  type CmssyDraftRouteConfig = Pick<CmssyConfig, "draftSecret"> & {
@@ -12,4 +12,66 @@ declare function createDraftRoute(config: CmssyDraftRouteConfig): (request: Requ
12
12
 
13
13
  declare function isCmssyEditMode(): Promise<boolean>;
14
14
 
15
- export { type CmssyDraftRouteConfig, createDraftRoute, isCmssyEditMode };
15
+ interface CmssyLayoutSlotProps {
16
+ config: CmssyConfig;
17
+ blocks: BlockDefinition[];
18
+ /**
19
+ * Which layout position to render. Six exist - `top`, `header`,
20
+ * `sidebar_left`, `sidebar_right`, `footer`, `bottom` - and most sites use
21
+ * two of them; the type is what tells you about the other four.
22
+ */
23
+ position: LayoutPosition;
24
+ /**
25
+ * The catch-all segments of the route rendering this slot, **as routed**.
26
+ * The language prefix in them IS the language, and reading it here keeps the
27
+ * route statically renderable.
28
+ *
29
+ * Omit it only where there are no params to read - a root layout - and the
30
+ * language falls back to the header the middleware set, which means calling
31
+ * `headers()` and giving up static rendering for the whole route. The version
32
+ * of this component removed in 10.0 had no choice about that; this one does.
33
+ */
34
+ path?: string[];
35
+ /** The page whose layout to render. Defaults to the site-wide header/footer. */
36
+ page?: string;
37
+ /**
38
+ * The client wrapper around `CmssyLazyLayout`, rendered in edit mode.
39
+ *
40
+ * Required, and a component rather than a loader: the block registry has to
41
+ * be imported lazily ON THE CLIENT, and a function cannot cross the server
42
+ * boundary. Without it the editor gets a header it can select and not fill -
43
+ * which is why this is a type error rather than a runtime warning.
44
+ */
45
+ editable: ComponentType<{
46
+ groups: CmssyLayoutGroup[];
47
+ position: string;
48
+ locale: string;
49
+ defaultLocale: string;
50
+ enabledLocales: string[];
51
+ edit: {
52
+ editorOrigin: string;
53
+ };
54
+ data?: Record<string, unknown>;
55
+ resolvedContent?: Record<string, Record<string, unknown>>;
56
+ appContext?: Record<string, unknown>;
57
+ }>;
58
+ /** Forwarded untouched to every block as `context.app`. */
59
+ appContext?: Record<string, unknown>;
60
+ }
61
+ /**
62
+ * The site-wide header and footer, which are layout **blocks** rather than
63
+ * markup the app owns. Rendered the way each mode needs them:
64
+ *
65
+ * - published traffic: server-rendered blocks, no client cost;
66
+ * - the editor: the same blocks through the edit bridge, fetched with the
67
+ * preview secret and handed their server-resolved content, so a relation
68
+ * field shows records rather than the ids it stores.
69
+ *
70
+ * Getting any of that wrong is invisible - the site looks right while the
71
+ * editor shows a header it cannot fill, or the published version of one. Every
72
+ * app that wrote this by hand between 10.0 and 10.9 got at least one of the
73
+ * three wrong, which is why it is back.
74
+ */
75
+ declare function CmssyLayoutSlot({ config, blocks, position, path, page, editable: Editable, appContext, }: CmssyLayoutSlotProps): Promise<react_jsx_runtime.JSX.Element>;
76
+
77
+ export { type CmssyDraftRouteConfig, CmssyLayoutSlot, type CmssyLayoutSlotProps, createDraftRoute, isCmssyEditMode };
package/dist/server.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { C as CmssyAppContext, a as CmssyEditorProps, b as CreateCmssyPageOptions, c as createCmssyEditPage, d as createCmssyPage } from './index-B64zk2X7.js';
2
- import { CmssyConfig } from '@cmssy/core';
3
- import 'react/jsx-runtime';
4
- import 'react';
5
- import '@cmssy/react';
2
+ import { CmssyConfig, LayoutPosition } from '@cmssy/core';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ComponentType } from 'react';
5
+ import { BlockDefinition, CmssyLayoutGroup } from '@cmssy/react';
6
6
  import '@cmssy/react/client';
7
7
 
8
8
  type CmssyDraftRouteConfig = Pick<CmssyConfig, "draftSecret"> & {
@@ -12,4 +12,66 @@ declare function createDraftRoute(config: CmssyDraftRouteConfig): (request: Requ
12
12
 
13
13
  declare function isCmssyEditMode(): Promise<boolean>;
14
14
 
15
- export { type CmssyDraftRouteConfig, createDraftRoute, isCmssyEditMode };
15
+ interface CmssyLayoutSlotProps {
16
+ config: CmssyConfig;
17
+ blocks: BlockDefinition[];
18
+ /**
19
+ * Which layout position to render. Six exist - `top`, `header`,
20
+ * `sidebar_left`, `sidebar_right`, `footer`, `bottom` - and most sites use
21
+ * two of them; the type is what tells you about the other four.
22
+ */
23
+ position: LayoutPosition;
24
+ /**
25
+ * The catch-all segments of the route rendering this slot, **as routed**.
26
+ * The language prefix in them IS the language, and reading it here keeps the
27
+ * route statically renderable.
28
+ *
29
+ * Omit it only where there are no params to read - a root layout - and the
30
+ * language falls back to the header the middleware set, which means calling
31
+ * `headers()` and giving up static rendering for the whole route. The version
32
+ * of this component removed in 10.0 had no choice about that; this one does.
33
+ */
34
+ path?: string[];
35
+ /** The page whose layout to render. Defaults to the site-wide header/footer. */
36
+ page?: string;
37
+ /**
38
+ * The client wrapper around `CmssyLazyLayout`, rendered in edit mode.
39
+ *
40
+ * Required, and a component rather than a loader: the block registry has to
41
+ * be imported lazily ON THE CLIENT, and a function cannot cross the server
42
+ * boundary. Without it the editor gets a header it can select and not fill -
43
+ * which is why this is a type error rather than a runtime warning.
44
+ */
45
+ editable: ComponentType<{
46
+ groups: CmssyLayoutGroup[];
47
+ position: string;
48
+ locale: string;
49
+ defaultLocale: string;
50
+ enabledLocales: string[];
51
+ edit: {
52
+ editorOrigin: string;
53
+ };
54
+ data?: Record<string, unknown>;
55
+ resolvedContent?: Record<string, Record<string, unknown>>;
56
+ appContext?: Record<string, unknown>;
57
+ }>;
58
+ /** Forwarded untouched to every block as `context.app`. */
59
+ appContext?: Record<string, unknown>;
60
+ }
61
+ /**
62
+ * The site-wide header and footer, which are layout **blocks** rather than
63
+ * markup the app owns. Rendered the way each mode needs them:
64
+ *
65
+ * - published traffic: server-rendered blocks, no client cost;
66
+ * - the editor: the same blocks through the edit bridge, fetched with the
67
+ * preview secret and handed their server-resolved content, so a relation
68
+ * field shows records rather than the ids it stores.
69
+ *
70
+ * Getting any of that wrong is invisible - the site looks right while the
71
+ * editor shows a header it cannot fill, or the published version of one. Every
72
+ * app that wrote this by hand between 10.0 and 10.9 got at least one of the
73
+ * three wrong, which is why it is back.
74
+ */
75
+ declare function CmssyLayoutSlot({ config, blocks, position, path, page, editable: Editable, appContext, }: CmssyLayoutSlotProps): Promise<react_jsx_runtime.JSX.Element>;
76
+
77
+ export { type CmssyDraftRouteConfig, CmssyLayoutSlot, type CmssyLayoutSlotProps, createDraftRoute, isCmssyEditMode };
package/dist/server.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import 'server-only';
2
2
  import { draftMode, headers } from 'next/headers';
3
3
  import { notFound, redirect } from 'next/navigation';
4
- import { createCmssyClient, CmssyServerPage } from '@cmssy/react';
4
+ import { createCmssyClient, CmssyServerPage, CmssyServerLayout, resolveEditorLayoutBlockData } from '@cmssy/react';
5
5
  import { CmssyLocaleProvider } from '@cmssy/react/internal';
6
6
  import { resolveEditorBlockData } from '@cmssy/react/internal-server';
7
- import { isDevelopment, resolveSiteLocales, splitLocaleFromPath, fetchPage, resolveForms, toCspOrigin, cmssySecretsMatch } from '@cmssy/core/internal';
7
+ import { isDevelopment, resolveSiteLocales, splitLocaleFromPath, fetchPage, resolveForms, toCspOrigin, cmssySecretsMatch, fetchLayouts } from '@cmssy/core/internal';
8
8
  import { CMSSY_SECRET_QUERY_PARAM, resolveEditorOrigin, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM } from '@cmssy/core';
9
9
  import { jsx } from 'react/jsx-runtime';
10
+ import { resolveSiteLocales as resolveSiteLocales$1, splitLocaleFromPath as splitLocaleFromPath$1, CMSSY_LOCALE_HEADER } from '@cmssy/core/internal/locale';
10
11
 
11
12
  // src/server.ts
12
13
  function hasEditFlag(value) {
@@ -247,5 +248,68 @@ async function isCmssyEditMode() {
247
248
  const h = await headers();
248
249
  return h.get(CMSSY_EDIT_HEADER) === "1";
249
250
  }
251
+ async function CmssyLayoutSlot({
252
+ config,
253
+ blocks,
254
+ position,
255
+ path,
256
+ page = "/",
257
+ editable: Editable,
258
+ appContext
259
+ }) {
260
+ const editMode = await isCmssyEditMode();
261
+ const [groups, siteLocales] = await Promise.all([
262
+ fetchLayouts(config, page, {
263
+ previewSecret: editMode ? config.draftSecret : void 0
264
+ }),
265
+ resolveSiteLocales$1(config)
266
+ ]);
267
+ const locale = path ? splitLocaleFromPath$1(path, siteLocales).locale : await localeFromHeader(siteLocales.defaultLocale);
268
+ if (!editMode) {
269
+ return /* @__PURE__ */ jsx(
270
+ CmssyServerLayout,
271
+ {
272
+ groups,
273
+ blocks,
274
+ position,
275
+ locale,
276
+ defaultLocale: siteLocales.defaultLocale,
277
+ enabledLocales: siteLocales.locales,
278
+ config,
279
+ appContext
280
+ }
281
+ );
282
+ }
283
+ const origin = resolveEditorOrigin(config.editorOrigin);
284
+ const editorData = await resolveEditorLayoutBlockData({
285
+ groups,
286
+ blocks,
287
+ position,
288
+ locale,
289
+ defaultLocale: siteLocales.defaultLocale,
290
+ enabledLocales: siteLocales.locales,
291
+ isPreview: true,
292
+ config,
293
+ appContext
294
+ });
295
+ return /* @__PURE__ */ jsx(
296
+ Editable,
297
+ {
298
+ groups,
299
+ position,
300
+ locale,
301
+ defaultLocale: siteLocales.defaultLocale,
302
+ enabledLocales: siteLocales.locales,
303
+ edit: { editorOrigin: (Array.isArray(origin) ? origin[0] : origin) ?? "" },
304
+ data: editorData.data,
305
+ resolvedContent: editorData.content,
306
+ appContext
307
+ }
308
+ );
309
+ }
310
+ async function localeFromHeader(defaultLocale) {
311
+ const { headers: headers3 } = await import('next/headers');
312
+ return (await headers3()).get(CMSSY_LOCALE_HEADER) || defaultLocale;
313
+ }
250
314
 
251
- export { createCmssyEditPage, createCmssyPage, createDraftRoute, isCmssyEditMode };
315
+ export { CmssyLayoutSlot, createCmssyEditPage, createCmssyPage, createDraftRoute, isCmssyEditMode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/next",
3
- "version": "10.9.1",
3
+ "version": "10.10.0",
4
4
  "description": "Next.js App Router bindings for cmssy headless sites (createCmssyPage + draft preview)",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -65,12 +65,12 @@
65
65
  "tsup": "^8.3.0",
66
66
  "typescript": "^5.6.0",
67
67
  "vitest": "^2.1.0",
68
- "@cmssy/react": "10.9.1"
68
+ "@cmssy/react": "10.10.0"
69
69
  },
70
70
  "dependencies": {
71
71
  "@cmssy/types": "0.32.0",
72
72
  "server-only": "^0.0.1",
73
- "@cmssy/core": "10.9.1"
73
+ "@cmssy/core": "10.10.0"
74
74
  },
75
75
  "scripts": {
76
76
  "build": "tsup",