@cmssy/astro 11.0.0 → 11.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var core = require('@cmssy/core');
4
4
  var internal = require('@cmssy/core/internal');
5
+ var react = require('@cmssy/react');
5
6
 
6
7
  // src/middleware.ts
7
8
  var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
@@ -18,7 +19,9 @@ function cmssyMiddleware(config, options = {}) {
18
19
  if (verified && !pathname.startsWith(CMSSY_EDIT_PATH_PREFIX)) {
19
20
  context.request.headers.set(core.CMSSY_EDIT_HEADER, "1");
20
21
  const target = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}${context.url.search}`;
21
- const response = await context.rewrite(target);
22
+ const response = await context.rewrite(
23
+ new Request(new URL(target, context.url), context.request)
24
+ );
22
25
  core.applyCmssyCsp(response, { editorOrigin: config.editorOrigin });
23
26
  return response;
24
27
  }
@@ -47,25 +50,57 @@ function cmssyMiddleware(config, options = {}) {
47
50
  return next();
48
51
  };
49
52
  }
50
- async function loadCmssyPage(config, request, url) {
51
- const isEdit = request.headers.get(core.CMSSY_EDIT_HEADER) === "1";
52
- const siteLocales = await internal.resolveSiteLocales(config);
53
+ async function loadCmssyPage(config, request, url, options = {}) {
54
+ const isEdit = request.headers.get(core.CMSSY_EDIT_HEADER) === "1" || await core.isVerifiedEditUrl(url, config);
53
55
  const segments = url.pathname.replace(/^\/cmssy-edit/, "").split("/").filter(Boolean);
54
- const fromPath = internal.splitLocaleFromPath(segments, siteLocales);
55
- const locale = request.headers.get(internal.CMSSY_LOCALE_HEADER) ?? fromPath.locale;
56
- const path = fromPath.path;
57
- const previewSecret = isEdit ? config.draftSecret : void 0;
58
- const [page, layouts] = await Promise.all([
59
- internal.fetchPage(config, path, { previewSecret }),
60
- internal.fetchLayouts(config, path, { previewSecret })
61
- ]);
56
+ const positions = options.positions ?? ["header", "footer"];
57
+ const blocks = options.blocks ?? [];
58
+ const headerLocale = request.headers.get(internal.CMSSY_LOCALE_HEADER) ?? void 0;
59
+ const slot = await react.resolveCmssyLayoutSlot(config, {
60
+ position: positions[0] ?? "header",
61
+ blocks,
62
+ editMode: isEdit,
63
+ path: segments,
64
+ locale: headerLocale,
65
+ appContext: options.appContext
66
+ });
67
+ const page = await internal.fetchPage(config, slot.path, {
68
+ previewSecret: isEdit ? config.draftSecret : void 0
69
+ });
70
+ let editorData;
71
+ if (isEdit && slot.data && slot.resolvedContent) {
72
+ editorData = {
73
+ [positions[0] ?? "header"]: {
74
+ data: slot.data,
75
+ resolvedContent: slot.resolvedContent
76
+ }
77
+ };
78
+ for (const position of positions.slice(1)) {
79
+ const extra = await react.resolveCmssyLayoutSlot(config, {
80
+ position,
81
+ blocks,
82
+ editMode: true,
83
+ path: segments,
84
+ locale: headerLocale,
85
+ appContext: options.appContext
86
+ });
87
+ if (extra.data && extra.resolvedContent) {
88
+ editorData[position] = {
89
+ data: extra.data,
90
+ resolvedContent: extra.resolvedContent
91
+ };
92
+ }
93
+ }
94
+ }
62
95
  return {
63
96
  page,
64
- layouts,
65
- locale,
66
- defaultLocale: siteLocales.defaultLocale,
67
- enabledLocales: siteLocales.locales,
68
- isEdit
97
+ layouts: slot.groups,
98
+ locale: slot.locale,
99
+ defaultLocale: slot.defaultLocale,
100
+ enabledLocales: slot.enabledLocales,
101
+ isEdit,
102
+ editorData,
103
+ editorOrigin: slot.editorOrigin
69
104
  };
70
105
  }
71
106
 
package/dist/index.d.cts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
2
2
  export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
3
+ import { BlockDefinition } from '@cmssy/react';
3
4
 
4
5
  declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
5
6
  interface CmssyMiddlewareOptions {
@@ -13,7 +14,7 @@ interface CmssyMiddlewareOptions {
13
14
  interface AstroContextLike {
14
15
  url: URL;
15
16
  request: Request;
16
- rewrite: (path: string) => Promise<Response> | Response;
17
+ rewrite: (target: string | URL | Request) => Promise<Response> | Response;
17
18
  }
18
19
  /**
19
20
  * The whole middleware a cmssy Astro app needs, in the order it has to happen:
@@ -40,14 +41,40 @@ interface CmssyPageResult {
40
41
  enabledLocales: string[];
41
42
  /** True for a verified editor request. The edit route renders the bridge. */
42
43
  isEdit: boolean;
44
+ /**
45
+ * Edit mode only, and only when `blocks` was passed: what the canvas needs,
46
+ * **keyed by position**. The header and the footer hold different blocks, so
47
+ * they resolve to different data - handing the footer the header's is the
48
+ * quiet version of this bug.
49
+ *
50
+ * Without `resolvedContent` a relation field shows the raw ids it stores,
51
+ * which is what this adapter did until now.
52
+ */
53
+ editorData?: Record<string, CmssyLayoutEditorData>;
54
+ editorOrigin?: string | string[];
55
+ }
56
+ interface CmssyLayoutEditorData {
57
+ data: Record<string, unknown>;
58
+ resolvedContent: Record<string, Record<string, unknown>>;
59
+ }
60
+ interface LoadCmssyPageOptions {
61
+ /**
62
+ * The block registry. Required to resolve the editor's layout data; omit it
63
+ * and the layouts still render, but the editor cannot fill them.
64
+ */
65
+ blocks?: BlockDefinition[];
66
+ /** Which positions the editor renders. Defaults to the header and footer. */
67
+ positions?: string[];
68
+ appContext?: Record<string, unknown>;
43
69
  }
44
70
  /**
45
- * Everything a cmssy page needs, from a plain Request. No framework globals: the
46
- * locale comes from the header the middleware set (falling back to the path, so
47
- * a prerendered page still knows its language), and the editor flag comes from
48
- * the same signal the Next adapter uses - because it is the same protocol, not
49
- * a Next protocol.
71
+ * Everything a cmssy page needs, from a plain Request. No framework globals.
72
+ *
73
+ * The layout half is `resolveCmssyLayoutSlot` from `@cmssy/react` - the same
74
+ * function the Next adapter uses, so the preview secret, the language and the
75
+ * editor data are decided in one place for all three frameworks rather than
76
+ * three times with two of them wrong.
50
77
  */
51
- declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
78
+ declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL, options?: LoadCmssyPageOptions): Promise<CmssyPageResult>;
52
79
 
53
80
  export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, cmssyMiddleware, loadCmssyPage };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CmssyConfig, CmssyPageData, CmssyLayoutGroup } from '@cmssy/core';
2
2
  export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CmssyBlockContext, CmssyConfig, CmssyEnvConfig, CmssyLayoutGroup, CmssyPageData, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
3
+ import { BlockDefinition } from '@cmssy/react';
3
4
 
4
5
  declare const CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
5
6
  interface CmssyMiddlewareOptions {
@@ -13,7 +14,7 @@ interface CmssyMiddlewareOptions {
13
14
  interface AstroContextLike {
14
15
  url: URL;
15
16
  request: Request;
16
- rewrite: (path: string) => Promise<Response> | Response;
17
+ rewrite: (target: string | URL | Request) => Promise<Response> | Response;
17
18
  }
18
19
  /**
19
20
  * The whole middleware a cmssy Astro app needs, in the order it has to happen:
@@ -40,14 +41,40 @@ interface CmssyPageResult {
40
41
  enabledLocales: string[];
41
42
  /** True for a verified editor request. The edit route renders the bridge. */
42
43
  isEdit: boolean;
44
+ /**
45
+ * Edit mode only, and only when `blocks` was passed: what the canvas needs,
46
+ * **keyed by position**. The header and the footer hold different blocks, so
47
+ * they resolve to different data - handing the footer the header's is the
48
+ * quiet version of this bug.
49
+ *
50
+ * Without `resolvedContent` a relation field shows the raw ids it stores,
51
+ * which is what this adapter did until now.
52
+ */
53
+ editorData?: Record<string, CmssyLayoutEditorData>;
54
+ editorOrigin?: string | string[];
55
+ }
56
+ interface CmssyLayoutEditorData {
57
+ data: Record<string, unknown>;
58
+ resolvedContent: Record<string, Record<string, unknown>>;
59
+ }
60
+ interface LoadCmssyPageOptions {
61
+ /**
62
+ * The block registry. Required to resolve the editor's layout data; omit it
63
+ * and the layouts still render, but the editor cannot fill them.
64
+ */
65
+ blocks?: BlockDefinition[];
66
+ /** Which positions the editor renders. Defaults to the header and footer. */
67
+ positions?: string[];
68
+ appContext?: Record<string, unknown>;
43
69
  }
44
70
  /**
45
- * Everything a cmssy page needs, from a plain Request. No framework globals: the
46
- * locale comes from the header the middleware set (falling back to the path, so
47
- * a prerendered page still knows its language), and the editor flag comes from
48
- * the same signal the Next adapter uses - because it is the same protocol, not
49
- * a Next protocol.
71
+ * Everything a cmssy page needs, from a plain Request. No framework globals.
72
+ *
73
+ * The layout half is `resolveCmssyLayoutSlot` from `@cmssy/react` - the same
74
+ * function the Next adapter uses, so the preview secret, the language and the
75
+ * editor data are decided in one place for all three frameworks rather than
76
+ * three times with two of them wrong.
50
77
  */
51
- declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL): Promise<CmssyPageResult>;
78
+ declare function loadCmssyPage(config: CmssyConfig, request: Request, url: URL, options?: LoadCmssyPageOptions): Promise<CmssyPageResult>;
52
79
 
53
80
  export { CMSSY_EDIT_PATH_PREFIX, type CmssyMiddlewareOptions, type CmssyPageResult, cmssyMiddleware, loadCmssyPage };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, isVerifiedEditUrl, applyCmssyCsp, CMSSY_SECRET_QUERY_PARAM } from '@cmssy/core';
2
2
  export { CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, createCmssyClient, defineCmssyConfig, localizeHref } from '@cmssy/core';
3
- import { CMSSY_LOCALE_HEADER, localeForPathname, isDevelopment, resolveSiteLocales, splitLocaleFromPath, fetchPage, fetchLayouts } from '@cmssy/core/internal';
3
+ import { CMSSY_LOCALE_HEADER, localeForPathname, isDevelopment, resolveSiteLocales, fetchPage } from '@cmssy/core/internal';
4
+ import { resolveCmssyLayoutSlot } from '@cmssy/react';
4
5
 
5
6
  // src/middleware.ts
6
7
  var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
@@ -17,7 +18,9 @@ function cmssyMiddleware(config, options = {}) {
17
18
  if (verified && !pathname.startsWith(CMSSY_EDIT_PATH_PREFIX)) {
18
19
  context.request.headers.set(CMSSY_EDIT_HEADER, "1");
19
20
  const target = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}${context.url.search}`;
20
- const response = await context.rewrite(target);
21
+ const response = await context.rewrite(
22
+ new Request(new URL(target, context.url), context.request)
23
+ );
21
24
  applyCmssyCsp(response, { editorOrigin: config.editorOrigin });
22
25
  return response;
23
26
  }
@@ -46,25 +49,57 @@ function cmssyMiddleware(config, options = {}) {
46
49
  return next();
47
50
  };
48
51
  }
49
- async function loadCmssyPage(config, request, url) {
50
- const isEdit = request.headers.get(CMSSY_EDIT_HEADER) === "1";
51
- const siteLocales = await resolveSiteLocales(config);
52
+ async function loadCmssyPage(config, request, url, options = {}) {
53
+ const isEdit = request.headers.get(CMSSY_EDIT_HEADER) === "1" || await isVerifiedEditUrl(url, config);
52
54
  const segments = url.pathname.replace(/^\/cmssy-edit/, "").split("/").filter(Boolean);
53
- const fromPath = splitLocaleFromPath(segments, siteLocales);
54
- const locale = request.headers.get(CMSSY_LOCALE_HEADER) ?? fromPath.locale;
55
- const path = fromPath.path;
56
- const previewSecret = isEdit ? config.draftSecret : void 0;
57
- const [page, layouts] = await Promise.all([
58
- fetchPage(config, path, { previewSecret }),
59
- fetchLayouts(config, path, { previewSecret })
60
- ]);
55
+ const positions = options.positions ?? ["header", "footer"];
56
+ const blocks = options.blocks ?? [];
57
+ const headerLocale = request.headers.get(CMSSY_LOCALE_HEADER) ?? void 0;
58
+ const slot = await resolveCmssyLayoutSlot(config, {
59
+ position: positions[0] ?? "header",
60
+ blocks,
61
+ editMode: isEdit,
62
+ path: segments,
63
+ locale: headerLocale,
64
+ appContext: options.appContext
65
+ });
66
+ const page = await fetchPage(config, slot.path, {
67
+ previewSecret: isEdit ? config.draftSecret : void 0
68
+ });
69
+ let editorData;
70
+ if (isEdit && slot.data && slot.resolvedContent) {
71
+ editorData = {
72
+ [positions[0] ?? "header"]: {
73
+ data: slot.data,
74
+ resolvedContent: slot.resolvedContent
75
+ }
76
+ };
77
+ for (const position of positions.slice(1)) {
78
+ const extra = await resolveCmssyLayoutSlot(config, {
79
+ position,
80
+ blocks,
81
+ editMode: true,
82
+ path: segments,
83
+ locale: headerLocale,
84
+ appContext: options.appContext
85
+ });
86
+ if (extra.data && extra.resolvedContent) {
87
+ editorData[position] = {
88
+ data: extra.data,
89
+ resolvedContent: extra.resolvedContent
90
+ };
91
+ }
92
+ }
93
+ }
61
94
  return {
62
95
  page,
63
- layouts,
64
- locale,
65
- defaultLocale: siteLocales.defaultLocale,
66
- enabledLocales: siteLocales.locales,
67
- isEdit
96
+ layouts: slot.groups,
97
+ locale: slot.locale,
98
+ defaultLocale: slot.defaultLocale,
99
+ enabledLocales: slot.enabledLocales,
100
+ isEdit,
101
+ editorData,
102
+ editorOrigin: slot.editorOrigin
68
103
  };
69
104
  }
70
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/astro",
3
- "version": "11.0.0",
3
+ "version": "11.1.1",
4
4
  "description": "Astro bindings for cmssy headless sites: middleware, page loader, sitemap and robots.",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -39,16 +39,18 @@
39
39
  "dist"
40
40
  ],
41
41
  "peerDependencies": {
42
- "astro": ">=4"
42
+ "astro": ">=4",
43
+ "@cmssy/react": "^11.1.1"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@types/node": "^20.0.0",
46
47
  "tsup": "^8.3.0",
47
48
  "typescript": "^5.6.0",
48
- "vitest": "^2.1.0"
49
+ "vitest": "^2.1.0",
50
+ "@cmssy/react": "11.1.1"
49
51
  },
50
52
  "dependencies": {
51
- "@cmssy/core": "11.0.0"
53
+ "@cmssy/core": "11.1.1"
52
54
  },
53
55
  "scripts": {
54
56
  "build": "tsup",