@cmssy/next 4.7.2 → 5.0.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/testing.cjs CHANGED
@@ -1,63 +1,10 @@
1
1
  'use strict';
2
2
 
3
- // src/testing/edit-smoke.ts
4
- var EDITOR_MARKER = /CmssyEditor|cmssy-edit/;
5
- var SERVER_CHROME = /<header|<footer/;
6
- async function html(url) {
7
- const response = await fetch(url, { redirect: "manual" });
8
- return { status: response.status, body: await response.text() };
9
- }
10
- async function checkCmssyEditMode(options) {
11
- const { baseUrl, secret, path = "/" } = options;
12
- const failures = [];
13
- const url = (suffix) => `${baseUrl.replace(/\/+$/, "")}${suffix}`;
14
- const publicPage = await html(url(path));
15
- if (publicPage.status !== 200) {
16
- failures.push(`public ${path}: expected 200, got ${publicPage.status}`);
17
- }
18
- if (EDITOR_MARKER.test(publicPage.body)) {
19
- failures.push(`public ${path}: the editor is mounted on a public page`);
20
- }
21
- const hasServerChrome = SERVER_CHROME.test(publicPage.body);
22
- const unverified = await html(url(`${path}?cmssyEdit=1`));
23
- if (EDITOR_MARKER.test(unverified.body)) {
24
- failures.push(
25
- `${path}?cmssyEdit=1: edit mode without a secret - an unverified request must not open the editor (CMS-948)`
26
- );
27
- }
28
- const verified = await html(
29
- url(`${path}?cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`)
30
- );
31
- if (verified.status !== 200) {
32
- failures.push(`edit ${path}: expected 200, got ${verified.status}`);
33
- }
34
- if (!EDITOR_MARKER.test(verified.body)) {
35
- failures.push(
36
- `edit ${path}: no editor in the response - is the /cmssy-edit route mounted?`
37
- );
38
- }
39
- if (hasServerChrome && SERVER_CHROME.test(verified.body)) {
40
- failures.push(
41
- `edit ${path}: the chrome is still server-rendered - the header and footer will be selectable but have no fields (is CMSSY_EDIT_HEADER set on the rewrite?)`
42
- );
43
- }
44
- const { localizedPath, localizedMarker } = options;
45
- if (localizedPath && localizedMarker) {
46
- const localized = await html(
47
- url(
48
- `${localizedPath}?cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`
49
- )
50
- );
51
- if (!EDITOR_MARKER.test(localized.body)) {
52
- failures.push(`edit ${localizedPath}: no editor in the response`);
53
- }
54
- if (!localized.body.includes(localizedMarker)) {
55
- failures.push(
56
- `edit ${localizedPath}: "${localizedMarker}" is missing - the preview renders in the default language, not the one the URL asks for`
57
- );
58
- }
59
- }
60
- return { ok: failures.length === 0, failures };
61
- }
3
+ var testing = require('@cmssy/core/testing');
62
4
 
63
- exports.checkCmssyEditMode = checkCmssyEditMode;
5
+
6
+
7
+ Object.defineProperty(exports, "checkCmssyEditMode", {
8
+ enumerable: true,
9
+ get: function () { return testing.checkCmssyEditMode; }
10
+ });
@@ -1,39 +1 @@
1
- interface EditSmokeOptions {
2
- /** A running build of the consumer app, e.g. http://localhost:3000. */
3
- baseUrl: string;
4
- /** The site's CMSSY_DRAFT_SECRET. Without it nothing can be verified. */
5
- secret: string;
6
- /** A published page to exercise. Defaults to "/". */
7
- path?: string;
8
- /**
9
- * The same page under a language prefix, e.g. "/no". Pass it on a site whose
10
- * URLs carry the language, and the check also proves the preview renders in
11
- * THAT language rather than the default one.
12
- */
13
- localizedPath?: string;
14
- /** A word only the localized page says - "Handlekurv", say. */
15
- localizedMarker?: string;
16
- }
17
- interface EditSmokeResult {
18
- ok: boolean;
19
- failures: string[];
20
- }
21
- /**
22
- * Proves a consumer app's EDIT path still works - the path a build cannot check,
23
- * because the site compiles and serves fine while being uneditable.
24
- *
25
- * It asserts three independent things:
26
- * 1. the public page renders WITHOUT the editor, chrome server-rendered;
27
- * 2. a bare `?cmssyEdit=1` does NOT enter edit mode (an unverified pair must
28
- * not open the door - CMS-948);
29
- * 3. a verified `cmssyEdit=1` + `cmssySecret` renders the editor AND moves the
30
- * chrome onto the edit bridge.
31
- *
32
- * Run it against a started production build:
33
- *
34
- * const result = await checkCmssyEditMode({ baseUrl, secret });
35
- * expect(result.failures).toEqual([]);
36
- */
37
- declare function checkCmssyEditMode(options: EditSmokeOptions): Promise<EditSmokeResult>;
38
-
39
- export { type EditSmokeOptions, type EditSmokeResult, checkCmssyEditMode };
1
+ export { EditSmokeOptions, EditSmokeResult, checkCmssyEditMode } from '@cmssy/core/testing';
package/dist/testing.d.ts CHANGED
@@ -1,39 +1 @@
1
- interface EditSmokeOptions {
2
- /** A running build of the consumer app, e.g. http://localhost:3000. */
3
- baseUrl: string;
4
- /** The site's CMSSY_DRAFT_SECRET. Without it nothing can be verified. */
5
- secret: string;
6
- /** A published page to exercise. Defaults to "/". */
7
- path?: string;
8
- /**
9
- * The same page under a language prefix, e.g. "/no". Pass it on a site whose
10
- * URLs carry the language, and the check also proves the preview renders in
11
- * THAT language rather than the default one.
12
- */
13
- localizedPath?: string;
14
- /** A word only the localized page says - "Handlekurv", say. */
15
- localizedMarker?: string;
16
- }
17
- interface EditSmokeResult {
18
- ok: boolean;
19
- failures: string[];
20
- }
21
- /**
22
- * Proves a consumer app's EDIT path still works - the path a build cannot check,
23
- * because the site compiles and serves fine while being uneditable.
24
- *
25
- * It asserts three independent things:
26
- * 1. the public page renders WITHOUT the editor, chrome server-rendered;
27
- * 2. a bare `?cmssyEdit=1` does NOT enter edit mode (an unverified pair must
28
- * not open the door - CMS-948);
29
- * 3. a verified `cmssyEdit=1` + `cmssySecret` renders the editor AND moves the
30
- * chrome onto the edit bridge.
31
- *
32
- * Run it against a started production build:
33
- *
34
- * const result = await checkCmssyEditMode({ baseUrl, secret });
35
- * expect(result.failures).toEqual([]);
36
- */
37
- declare function checkCmssyEditMode(options: EditSmokeOptions): Promise<EditSmokeResult>;
38
-
39
- export { type EditSmokeOptions, type EditSmokeResult, checkCmssyEditMode };
1
+ export { EditSmokeOptions, EditSmokeResult, checkCmssyEditMode } from '@cmssy/core/testing';
package/dist/testing.js CHANGED
@@ -1,61 +1 @@
1
- // src/testing/edit-smoke.ts
2
- var EDITOR_MARKER = /CmssyEditor|cmssy-edit/;
3
- var SERVER_CHROME = /<header|<footer/;
4
- async function html(url) {
5
- const response = await fetch(url, { redirect: "manual" });
6
- return { status: response.status, body: await response.text() };
7
- }
8
- async function checkCmssyEditMode(options) {
9
- const { baseUrl, secret, path = "/" } = options;
10
- const failures = [];
11
- const url = (suffix) => `${baseUrl.replace(/\/+$/, "")}${suffix}`;
12
- const publicPage = await html(url(path));
13
- if (publicPage.status !== 200) {
14
- failures.push(`public ${path}: expected 200, got ${publicPage.status}`);
15
- }
16
- if (EDITOR_MARKER.test(publicPage.body)) {
17
- failures.push(`public ${path}: the editor is mounted on a public page`);
18
- }
19
- const hasServerChrome = SERVER_CHROME.test(publicPage.body);
20
- const unverified = await html(url(`${path}?cmssyEdit=1`));
21
- if (EDITOR_MARKER.test(unverified.body)) {
22
- failures.push(
23
- `${path}?cmssyEdit=1: edit mode without a secret - an unverified request must not open the editor (CMS-948)`
24
- );
25
- }
26
- const verified = await html(
27
- url(`${path}?cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`)
28
- );
29
- if (verified.status !== 200) {
30
- failures.push(`edit ${path}: expected 200, got ${verified.status}`);
31
- }
32
- if (!EDITOR_MARKER.test(verified.body)) {
33
- failures.push(
34
- `edit ${path}: no editor in the response - is the /cmssy-edit route mounted?`
35
- );
36
- }
37
- if (hasServerChrome && SERVER_CHROME.test(verified.body)) {
38
- failures.push(
39
- `edit ${path}: the chrome is still server-rendered - the header and footer will be selectable but have no fields (is CMSSY_EDIT_HEADER set on the rewrite?)`
40
- );
41
- }
42
- const { localizedPath, localizedMarker } = options;
43
- if (localizedPath && localizedMarker) {
44
- const localized = await html(
45
- url(
46
- `${localizedPath}?cmssyEdit=1&cmssySecret=${encodeURIComponent(secret)}`
47
- )
48
- );
49
- if (!EDITOR_MARKER.test(localized.body)) {
50
- failures.push(`edit ${localizedPath}: no editor in the response`);
51
- }
52
- if (!localized.body.includes(localizedMarker)) {
53
- failures.push(
54
- `edit ${localizedPath}: "${localizedMarker}" is missing - the preview renders in the default language, not the one the URL asks for`
55
- );
56
- }
57
- }
58
- return { ok: failures.length === 0, failures };
59
- }
60
-
61
- export { checkCmssyEditMode };
1
+ export { checkCmssyEditMode } from '@cmssy/core/testing';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmssy/next",
3
- "version": "4.7.2",
3
+ "version": "5.0.1",
4
4
  "description": "Next.js App Router bindings for cmssy headless sites (createCmssyPage + draft preview)",
5
5
  "keywords": [
6
6
  "cmssy",
@@ -28,6 +28,16 @@
28
28
  "import": "./dist/index.js",
29
29
  "require": "./dist/index.cjs"
30
30
  },
31
+ "./server": {
32
+ "types": "./dist/server.d.ts",
33
+ "import": "./dist/server.js",
34
+ "require": "./dist/server.cjs"
35
+ },
36
+ "./middleware": {
37
+ "types": "./dist/middleware.d.ts",
38
+ "import": "./dist/middleware.js",
39
+ "require": "./dist/middleware.cjs"
40
+ },
31
41
  "./client": {
32
42
  "types": "./dist/client.d.ts",
33
43
  "import": "./dist/client.js",
@@ -37,11 +47,6 @@
37
47
  "types": "./dist/testing.d.ts",
38
48
  "import": "./dist/testing.js",
39
49
  "require": "./dist/testing.cjs"
40
- },
41
- "./preset": {
42
- "types": "./dist/preset.d.ts",
43
- "import": "./dist/preset.js",
44
- "require": "./dist/preset.cjs"
45
50
  }
46
51
  },
47
52
  "main": "./dist/index.cjs",
@@ -51,7 +56,7 @@
51
56
  "dist"
52
57
  ],
53
58
  "peerDependencies": {
54
- "@cmssy/react": "^4.7.2",
59
+ "@cmssy/react": "^5.0.1",
55
60
  "next": ">=15",
56
61
  "react": "^18.2.0 || ^19.0.0",
57
62
  "react-dom": "^18.2.0 || ^19.0.0"
@@ -65,11 +70,12 @@
65
70
  "tsup": "^8.3.0",
66
71
  "typescript": "^5.6.0",
67
72
  "vitest": "^2.1.0",
68
- "@cmssy/react": "4.7.2"
73
+ "@cmssy/react": "5.0.1"
69
74
  },
70
75
  "dependencies": {
71
- "jose": "^6.2.3",
72
- "@cmssy/types": "0.27.0"
76
+ "@cmssy/types": "0.27.0",
77
+ "server-only": "^0.0.1",
78
+ "@cmssy/core": "5.0.1"
73
79
  },
74
80
  "scripts": {
75
81
  "build": "tsup",
@@ -1,63 +0,0 @@
1
- import { CmssyAuthConfig } from '@cmssy/types';
2
-
3
- declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
4
- declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
5
- interface CmssyNextConfig {
6
- /**
7
- * Full GraphQL delivery endpoint. Defaults to the cmssy cloud endpoint
8
- * (`https://api.cmssy.io/graphql`); set it only for self-hosted / staging.
9
- */
10
- apiUrl?: string;
11
- /** Organization slug - part of the org-scoped delivery path. */
12
- org: string;
13
- workspaceSlug: string;
14
- draftSecret: string;
15
- /**
16
- * A cmssy API token (`cs_…`) that opts this app into the editor-controlled dev
17
- * preview. In development only, the SDK sends it on every page fetch so the
18
- * backend can resolve the token's user and honour that user's dev-preview flag
19
- * (toggled from the editor's dev-mode switch): flag on + a saved dev draft ⇒
20
- * the draft overlay renders, otherwise published content. Server-only (never
21
- * reaches the client); ignored outside development. See the Quickstart
22
- * "Dev preview" section.
23
- */
24
- devToken?: string;
25
- /**
26
- * Origin allowed to frame your app in the editor. Defaults to
27
- * {@link DEFAULT_CMSSY_EDITOR_ORIGINS}; set it only for self-hosted admins.
28
- */
29
- editorOrigin?: string | string[];
30
- /**
31
- * Canonical absolute site URL (e.g. https://cmssy.com), used by
32
- * createCmssyRobots / createCmssySitemap. When omitted the helpers derive the
33
- * origin from the request `host` header at render time (multi-domain safe).
34
- */
35
- siteUrl?: string;
36
- auth?: CmssyAuthConfig;
37
- defaultLocale?: string;
38
- /** All languages enabled on the workspace; exposed to blocks via context.locale.enabled. */
39
- enabledLocales?: string[];
40
- resolveLocale?: () => string | Promise<string>;
41
- }
42
- /**
43
- * Env-shaped input for {@link defineCmssyConfig}: the required string fields are
44
- * widened to `string | undefined` so a config can pass `process.env.*` straight
45
- * through without a `?? ""` fallback (which would mask a missing value as an
46
- * empty string) or a cast.
47
- */
48
- type CmssyEnvConfig = Omit<CmssyNextConfig, "org" | "workspaceSlug" | "draftSecret"> & {
49
- org?: string;
50
- workspaceSlug?: string;
51
- draftSecret?: string;
52
- };
53
- /**
54
- * Validates an env-sourced config and returns a strictly-typed
55
- * {@link CmssyNextConfig}. Pass raw `process.env.*` values; this throws a clear,
56
- * actionable error listing any missing required variables (rendered by the
57
- * Next.js error overlay / boundary), so the app fails fast instead of running
58
- * with silently-empty config.
59
- */
60
- declare function defineCmssyConfig(config: CmssyEnvConfig): CmssyNextConfig;
61
- declare function assertAuthConfig(config: CmssyNextConfig): CmssyAuthConfig;
62
-
63
- export { type CmssyNextConfig as C, DEFAULT_CMSSY_EDITOR_ORIGINS as D, type CmssyEnvConfig as a, assertAuthConfig as b, defineCmssyConfig as d, resolveEditorOrigin as r };
@@ -1,63 +0,0 @@
1
- import { CmssyAuthConfig } from '@cmssy/types';
2
-
3
- declare const DEFAULT_CMSSY_EDITOR_ORIGINS: string[];
4
- declare function resolveEditorOrigin(editorOrigin: string | string[] | undefined): string | string[];
5
- interface CmssyNextConfig {
6
- /**
7
- * Full GraphQL delivery endpoint. Defaults to the cmssy cloud endpoint
8
- * (`https://api.cmssy.io/graphql`); set it only for self-hosted / staging.
9
- */
10
- apiUrl?: string;
11
- /** Organization slug - part of the org-scoped delivery path. */
12
- org: string;
13
- workspaceSlug: string;
14
- draftSecret: string;
15
- /**
16
- * A cmssy API token (`cs_…`) that opts this app into the editor-controlled dev
17
- * preview. In development only, the SDK sends it on every page fetch so the
18
- * backend can resolve the token's user and honour that user's dev-preview flag
19
- * (toggled from the editor's dev-mode switch): flag on + a saved dev draft ⇒
20
- * the draft overlay renders, otherwise published content. Server-only (never
21
- * reaches the client); ignored outside development. See the Quickstart
22
- * "Dev preview" section.
23
- */
24
- devToken?: string;
25
- /**
26
- * Origin allowed to frame your app in the editor. Defaults to
27
- * {@link DEFAULT_CMSSY_EDITOR_ORIGINS}; set it only for self-hosted admins.
28
- */
29
- editorOrigin?: string | string[];
30
- /**
31
- * Canonical absolute site URL (e.g. https://cmssy.com), used by
32
- * createCmssyRobots / createCmssySitemap. When omitted the helpers derive the
33
- * origin from the request `host` header at render time (multi-domain safe).
34
- */
35
- siteUrl?: string;
36
- auth?: CmssyAuthConfig;
37
- defaultLocale?: string;
38
- /** All languages enabled on the workspace; exposed to blocks via context.locale.enabled. */
39
- enabledLocales?: string[];
40
- resolveLocale?: () => string | Promise<string>;
41
- }
42
- /**
43
- * Env-shaped input for {@link defineCmssyConfig}: the required string fields are
44
- * widened to `string | undefined` so a config can pass `process.env.*` straight
45
- * through without a `?? ""` fallback (which would mask a missing value as an
46
- * empty string) or a cast.
47
- */
48
- type CmssyEnvConfig = Omit<CmssyNextConfig, "org" | "workspaceSlug" | "draftSecret"> & {
49
- org?: string;
50
- workspaceSlug?: string;
51
- draftSecret?: string;
52
- };
53
- /**
54
- * Validates an env-sourced config and returns a strictly-typed
55
- * {@link CmssyNextConfig}. Pass raw `process.env.*` values; this throws a clear,
56
- * actionable error listing any missing required variables (rendered by the
57
- * Next.js error overlay / boundary), so the app fails fast instead of running
58
- * with silently-empty config.
59
- */
60
- declare function defineCmssyConfig(config: CmssyEnvConfig): CmssyNextConfig;
61
- declare function assertAuthConfig(config: CmssyNextConfig): CmssyAuthConfig;
62
-
63
- export { type CmssyNextConfig as C, DEFAULT_CMSSY_EDITOR_ORIGINS as D, type CmssyEnvConfig as a, assertAuthConfig as b, defineCmssyConfig as d, resolveEditorOrigin as r };
package/dist/preset.cjs DELETED
@@ -1,235 +0,0 @@
1
- 'use strict';
2
-
3
- var server = require('next/server');
4
- var react = require('@cmssy/react');
5
- var headers = require('next/headers');
6
- var jsxRuntime = require('react/jsx-runtime');
7
-
8
- // src/preset/proxy.ts
9
-
10
- // src/config.ts
11
- var DEFAULT_CMSSY_EDITOR_ORIGINS = [
12
- "https://cmssy.io",
13
- "https://www.cmssy.io"
14
- ];
15
- function parseEditorOriginEnv(raw) {
16
- if (!raw) return void 0;
17
- const parts = raw.split(",").map((o) => o.trim()).filter((o) => o.length > 0);
18
- if (parts.length === 0) return void 0;
19
- return parts.length === 1 ? parts[0] : parts;
20
- }
21
- function isDevelopment() {
22
- return typeof process !== "undefined" && process.env.NODE_ENV === "development";
23
- }
24
- function resolveEditorOrigin(editorOrigin) {
25
- const value = editorOrigin ?? (typeof process !== "undefined" ? parseEditorOriginEnv(process.env.CMSSY_EDITOR_ORIGIN) : void 0);
26
- if (value === void 0) {
27
- return isDevelopment() ? "*" : DEFAULT_CMSSY_EDITOR_ORIGINS;
28
- }
29
- if (Array.isArray(value)) {
30
- const cleaned = value.filter((o) => o && o.trim().length > 0);
31
- return cleaned.length > 0 ? cleaned : DEFAULT_CMSSY_EDITOR_ORIGINS;
32
- }
33
- return value.trim().length > 0 ? value : DEFAULT_CMSSY_EDITOR_ORIGINS;
34
- }
35
-
36
- // src/csp.ts
37
- function toCspOrigin(origin) {
38
- if (origin === "*") return "*";
39
- let parsed;
40
- try {
41
- parsed = new URL(origin);
42
- } catch {
43
- throw new Error(`cmssy: invalid editorOrigin "${origin}"`);
44
- }
45
- if (parsed.origin === "null") {
46
- throw new Error(`cmssy: editorOrigin "${origin}" has no usable origin`);
47
- }
48
- return parsed.origin;
49
- }
50
- function frameAncestors(editorOrigin) {
51
- const resolved = resolveEditorOrigin(editorOrigin);
52
- const origins = Array.isArray(resolved) ? resolved : [resolved];
53
- if (origins.length === 0) {
54
- throw new Error(
55
- "cmssy: editorOrigin must contain at least one valid origin"
56
- );
57
- }
58
- const normalized = origins.map((origin) => toCspOrigin(origin.trim()));
59
- return `frame-ancestors ${normalized.join(" ")}`;
60
- }
61
- function mergeFrameAncestors(existing, directive) {
62
- if (!existing) return directive;
63
- const kept = existing.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !/^frame-ancestors\b/i.test(part));
64
- kept.push(directive);
65
- return kept.join("; ");
66
- }
67
- function applyCmssyCsp(response, options) {
68
- const merged = mergeFrameAncestors(
69
- response.headers.get("Content-Security-Policy"),
70
- frameAncestors(options.editorOrigin)
71
- );
72
- response.headers.set("Content-Security-Policy", merged);
73
- response.headers.delete("X-Frame-Options");
74
- return response;
75
- }
76
-
77
- // src/secret-match.ts
78
- var MAX_SECRET_LENGTH = 256;
79
- async function cmssySecretsMatch(a, b) {
80
- if (a.length > MAX_SECRET_LENGTH || b.length > MAX_SECRET_LENGTH) {
81
- return false;
82
- }
83
- const encoder = new TextEncoder();
84
- const [ha, hb] = await Promise.all([
85
- crypto.subtle.digest("SHA-256", encoder.encode(a)),
86
- crypto.subtle.digest("SHA-256", encoder.encode(b))
87
- ]);
88
- const va = new Uint8Array(ha);
89
- const vb = new Uint8Array(hb);
90
- let diff = 0;
91
- for (let i = 0; i < va.length; i += 1) {
92
- diff |= (va[i] ?? 0) ^ (vb[i] ?? 0);
93
- }
94
- return diff === 0;
95
- }
96
-
97
- // src/edit-mode.ts
98
- var CMSSY_EDIT_HEADER = "x-cmssy-edit";
99
- var CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
100
- var CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
101
- async function isCmssyEditMode() {
102
- const h = await headers.headers();
103
- return h.get(CMSSY_EDIT_HEADER) === "1";
104
- }
105
- var CMSSY_EDIT_PATH_PREFIX = "/cmssy-edit";
106
- async function cmssyEditRewrite(request, config, options = {}) {
107
- const { pathname, searchParams } = request.nextUrl;
108
- if (pathname.startsWith(CMSSY_EDIT_PATH_PREFIX)) return null;
109
- if (!searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) return null;
110
- const provided = searchParams.get(CMSSY_SECRET_QUERY_PARAM);
111
- if (!provided || !config.draftSecret) return null;
112
- if (!await cmssySecretsMatch(provided, config.draftSecret)) return null;
113
- const url = request.nextUrl.clone();
114
- url.pathname = `${CMSSY_EDIT_PATH_PREFIX}${pathname === "/" ? "" : pathname}`;
115
- warnIfEditRouteMissing(url);
116
- return server.NextResponse.rewrite(
117
- url,
118
- options.requestHeaders ? { request: { headers: options.requestHeaders } } : void 0
119
- );
120
- }
121
- var probed = false;
122
- function warnIfEditRouteMissing(url) {
123
- if (process.env.NODE_ENV === "production" || probed) return;
124
- probed = true;
125
- void fetch(url, { method: "HEAD" }).then((response) => {
126
- if (response.status !== 404) return;
127
- console.error(
128
- `[cmssy] The editor request was rewritten to ${url.pathname}, but nothing is mounted there (404). Add the edit route:
129
-
130
- // app/cmssy-edit/[[...path]]/page.tsx
131
- export const dynamic = "force-dynamic";
132
- export default createCmssyEditPage(cmssy, blocks, { editor: CmssyEditor });
133
-
134
- Until then the editor preview stays blank.`
135
- );
136
- }).catch(() => {
137
- });
138
- }
139
- var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
140
- async function localeForPathname(config, pathname) {
141
- const siteLocales = await react.resolveSiteLocales(config);
142
- const segments = pathname.split("/").filter(Boolean);
143
- return react.splitLocaleFromPath(segments, siteLocales).locale;
144
- }
145
- async function getCmssyLocale(config, options) {
146
- const { headers: headers2 } = await import('next/headers');
147
- const headerList = await headers2();
148
- const fromHeader = headerList.get(CMSSY_LOCALE_HEADER);
149
- if (fromHeader) return fromHeader;
150
- const { defaultLocale } = await react.resolveSiteLocales(config);
151
- return defaultLocale;
152
- }
153
-
154
- // src/preset/proxy.ts
155
- function createCmssyProxy(config, options = {}) {
156
- return async function cmssyProxy(request) {
157
- const { pathname } = request.nextUrl;
158
- const requestHeaders = new Headers(request.headers);
159
- requestHeaders.delete(CMSSY_EDIT_HEADER);
160
- requestHeaders.delete(CMSSY_LOCALE_HEADER);
161
- const locale = await localeForPathname(config, pathname);
162
- requestHeaders.set(CMSSY_LOCALE_HEADER, locale);
163
- const editHeaders = new Headers(requestHeaders);
164
- editHeaders.set(CMSSY_EDIT_HEADER, "1");
165
- const editRewrite = await cmssyEditRewrite(request, config, {
166
- requestHeaders: editHeaders
167
- });
168
- if (editRewrite) {
169
- applyCmssyCsp(editRewrite, { editorOrigin: config.editorOrigin });
170
- return editRewrite;
171
- }
172
- if (options.stripLocalePrefix && pathname.startsWith(`/${locale}`)) {
173
- const { defaultLocale } = await react.resolveSiteLocales(config);
174
- if (locale !== defaultLocale) {
175
- const url = request.nextUrl.clone();
176
- url.pathname = pathname.slice(locale.length + 1) || "/";
177
- return server.NextResponse.rewrite(url, {
178
- request: { headers: requestHeaders }
179
- });
180
- }
181
- }
182
- return server.NextResponse.next({ request: { headers: requestHeaders } });
183
- };
184
- }
185
- var cmssyProxyMatcher = ["/((?!_next/|api/|.*\\..*).*)"];
186
- async function CmssyChrome({
187
- config,
188
- blocks,
189
- position,
190
- page = "/",
191
- editable
192
- }) {
193
- const editMode = await isCmssyEditMode();
194
- const [groups, locale, siteLocales] = await Promise.all([
195
- react.fetchLayouts(
196
- config,
197
- page,
198
- editMode ? { previewSecret: config.draftSecret } : void 0
199
- ),
200
- getCmssyLocale(config),
201
- react.resolveSiteLocales(config)
202
- ]);
203
- if (editMode && editable) {
204
- const origin = resolveEditorOrigin(config.editorOrigin);
205
- const Editable = editable;
206
- return /* @__PURE__ */ jsxRuntime.jsx(
207
- Editable,
208
- {
209
- groups,
210
- position,
211
- locale,
212
- defaultLocale: siteLocales.defaultLocale,
213
- enabledLocales: siteLocales.locales,
214
- edit: {
215
- editorOrigin: (Array.isArray(origin) ? origin[0] : origin) ?? ""
216
- }
217
- }
218
- );
219
- }
220
- return /* @__PURE__ */ jsxRuntime.jsx(
221
- react.CmssyServerLayout,
222
- {
223
- groups,
224
- blocks,
225
- position,
226
- locale,
227
- defaultLocale: siteLocales.defaultLocale,
228
- enabledLocales: siteLocales.locales
229
- }
230
- );
231
- }
232
-
233
- exports.CmssyChrome = CmssyChrome;
234
- exports.cmssyProxyMatcher = cmssyProxyMatcher;
235
- exports.createCmssyProxy = createCmssyProxy;